How to check if all the tables in database are modified after an Update activity is performed on columns of tables? - sql

I have to update all the tables having column name like '%DIV%' with a value DD wherever it is MG , I have written the script for it , but I am not getting the idea of how to verify if columns of all the tables are updated to value DD after the activity is performed. I have written this query .
SELECT 'SELECT '||OWNER||'.'||TABLE_NAME||', '||COLUMN_NAME||' FROM '||OWNER||'.'||TABLE_NAME||' WHERE '||COLUMN_NAME||' = ''MG'' ;'
FROM RADHA.CHANGE_TABLE
WHERE VALID_FLAG='Y'
I was planning to make a table structure like
OWNER TABLE_NAME PREV_COUNT
The PREV_COUNT will hold the count of rows having Column Value as MG and after the activity is performed , I will verify with following query if the corresponding rows have been updated to DD .
SELECT 'SELECT '||OWNER||'.'||TABLE_NAME||', '||COLUMN_NAME||' FROM '||OWNER||'.'||TABLE_NAME||' WHERE '||COLUMN_NAME||' = ''DD'' ;' FROM RADHA.CHANGE_TABLE WHERE VALID_FLAG='Y'
And the output of this query would go into table
OWNER TABLE_NAME NEW_COUNT
But I am not able to get how to fetch records from the Select query as it is the string which is written inside the select query but I want the result set such that I can insert the records in my table mentioned above, please guide how to approach further

I don't have your tables, but - based on Scott's sample schema, here's a script which search through all its tables for a column named JOB (line #8) and checks how many of them have value that looks like (hint: like) CLERK in it (line #12).
See how it works, adjust it so that it works for you.
SQL> DECLARE
2 l_str VARCHAR2(500);
3 l_cnt NUMBER := 0;
4 BEGIN
5 FOR cur_r IN (SELECT u.table_name, u.column_name
6 FROM user_tab_columns u, user_tables t
7 WHERE u.table_name = t.table_name
8 AND u.column_name = 'JOB'
9 )
10 LOOP
11 l_str := 'SELECT COUNT(*) FROM ' || cur_r.table_name ||
12 ' WHERE ' || cur_r.column_name || ' like (''%CLERK%'')';
13
14 EXECUTE IMMEDIATE (l_str) INTO l_cnt;
15
16 IF l_cnt > 0 THEN
17 dbms_output.put_line(l_cnt ||' : ' || cur_r.table_name);
18 END IF;
19 END LOOP;
20 END;
21 /
4 : EMP --> there are 4 CLERKs in the EMP table
PL/SQL procedure successfully completed.
SQL>

Related

How to read the same column from every table in a database?

I have a huge database with 400+ tables. Each table has the same column id for the Primary key and "timestamp_modify" in which the last change of the table is done.
So what I want are 2 things:
Now I want a list of all changes by ID and table name like:
Table | id | timestamp_modiy
Kid | 1 | 24.10.2021 00:01
Parent | 1000 | 24.10.2021 00:02
The only, very bad way I could come up with, is that I make a view in which I include every damn table by hand and read out the values...
Is there a better way?
How about a pipelined function?
Just setting datetime format (you don't have to do that):
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
Types:
SQL> create or replace type t_row as object
2 (table_name varchar2(30),
3 id number,
4 timestamp_modify date)
5 /
Type created.
SQL> create or replace type t_tab is table of t_row;
2 /
Type created.
Function: querying user_tab_columns, its cursor FOR loop fetches tables that contain both ID and TIMESTAMP_MODIFY columns, dynamically creates select statement to return the last (MAX function, to avoid too_many_rows) columns' values for the last TIMESTAMP_MODIFY value (returned by the subquery).
SQL> create or replace function f_test
2 return t_tab pipelined
3 as
4 l_str varchar2(500);
5 l_id number;
6 l_timestamp_modify date;
7 begin
8 for cur_r in (select table_name from user_tab_columns
9 where column_name = 'ID'
10 intersect
11 select table_name from user_tab_columns
12 where column_name = 'TIMESTAMP_MODIFY'
13 )
14 loop
15 l_str := 'select max(a.id) id, max(a.timestamp_modify) timestamp_modify ' ||
16 'from ' || cur_r.table_name || ' a ' ||
17 'where a.timestamp_modify = ' ||
18 ' (select max(b.timestamp_modify) ' ||
19 ' from ' || cur_r.table_name || ' b ' ||
20 ' where b.id = a.id)';
21 execute immediate l_str into l_id, l_timestamp_modify;
22 pipe row(t_row(cur_r.table_name, l_id, l_timestamp_modify));
23 end loop;
24 end;
25 /
Function created.
Testing:
SQL> select * from table(f_test);
TABLE_NAME ID TIMESTAMP_MODIFY
------------------------------ ---------- -------------------
TABA 1 24.10.2021 14:59:29
TAB_1 1 24.10.2021 15:03:16
TAB_2 25 24.10.2021 15:03:36
TEST 5 24.10.2021 15:04:24
SQL>
Yes, the only way is to union all all tables, like:
select id, timestamp_modify
from kid
union all
select id, timestamp_modify
from parent
union all
...
The performance will be awful, since all the tables will be scanned every time :(
I think that you might reconsider you db design...
You can build a procedure for this, but even so it will have some impact in performance. Although there is a loop, with SQL Dynamic, you might only need 400 iterations, and in each one you will insert all the ids of that table.
I am taking some assumptions
You want all the IDs and their corresponding timestamp_modify per table
I create a table to store the results. If you use it with the same name always it will recycle the object. If you not, you can keep a history
I am assuming that only one timestamp_modify row is present per ID
I filter only the tables of your schema that contain both columns.
The table contains also the table_name that you can identify where the record is coming from.
One example
create or replace procedure pr_build_output ( p_tmp_table in varchar2 default 'TMP_RESULT' )
is
vcounter pls_integer;
vsql clob;
vtimestamp date; -- or timestamp
begin
-- create table to store results
select count(*) into vcounter from all_tables where table_name = upper(p_tmp_table) and owner = 'MY_SCHEMA';
if vcounter = 1
then
execute immediate ' drop table '||p_tmp_table||' purge ' ;
end if;
vsql := ' create table '||p_tmp_table||'
( table_name varchar2(128) ,
id number,
timestamp_modify date -- or timestamp
) ';
execute immediate vsql ;
-- Populate rows
for h in
( select a.table_name from all_tables a
where a.owner = 'MY_SCHEMA'
and a.table_name in ( select distinct b.table_name from all_tab_columns b where b.owner = 'MY_SCHEMA'
and b.column_name = 'ID' and b.column_name = 'TIMESTAMP_MODIFY'
)
)
loop
vsql := ' insert into '||p_tmp_table||' ( table_name , id, timestamp_modify )
select '''||h.table_name||''' as table_name , id , timestamp_modify
from my_schema.'||h.table_name||'
' ;
execute immediate vsql ;
commit ;
end loop;
exception when others then raise;
end;
/

Filter column value while using ALL_TAB_COLUMNS

In SQL Oracle, is there a way to filter an ALL_TAB_COLUMNS SELECT statement, by the values in a specific column?
Yes; you'll need PL/SQL with dynamic SQL to do that.
Here's an example I use to search through current user's tables, check the ones that contain ENAME column which contains SCOTT string within. The result says that two tables (EMPLOYEE and EMP) contain one row with such a value.
Adjust it to your needs.
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 l_str VARCHAR2(500);
3 l_cnt NUMBER := 0;
4 BEGIN
5 FOR cur_r IN (SELECT u.table_name, u.column_name
6 FROM user_tab_columns u, user_tables t
7 WHERE u.table_name = t.table_name
8 AND u.column_name = 'ENAME'
9 )
10 LOOP
11 l_str := 'SELECT COUNT(*) FROM ' || cur_r.table_name ||
12 ' WHERE ' || cur_r.column_name || ' like (''%SCOTT%'')';
13
14 EXECUTE IMMEDIATE (l_str) INTO l_cnt;
15
16 IF l_cnt > 0 THEN
17 dbms_output.put_line(l_cnt ||' : ' || cur_r.table_name);
18 END IF;
19 END LOOP;
20 END;
21 /
1 : EMPLOYEE
1 : EMP
PL/SQL procedure successfully completed.
SQL>

Oracle:List all tables in the database with a column_value = '%value%' for a specific date range?

I need to list out all tables in the database where a particular employee made changes to records. I'm looking for a query in oracle to list out all tables where the employee_name column = 'person_name' and for date > 'sample_date'. is this possible ?
As you said - dynamic SQL helps. For example, based on Scott's sample schema, I'm searching for a table that contains both ENAME and HIREDATE columns with desired values (SCOTT and 09.12.1982 (dd.mm.yyyy)).
SQL> set serveroutput on
SQL>
SQL> DECLARE
2 l_str VARCHAR2(500);
3 l_cnt NUMBER := 0;
4 BEGIN
5 FOR cur_r IN (SELECT t.table_name, u1.column_name col1, u2.column_name col2
6 FROM user_tables t join user_tab_columns u1 on u1.table_name = t.table_name
7 join user_tab_columns u2 on u2.table_name = t.table_name
8 WHERE u1.column_name = 'ENAME'
9 AND u2.column_name = 'HIREDATE'
10 )
11 LOOP
12 l_str := 'SELECT COUNT(*) FROM ' || cur_r.table_name ||
13 ' WHERE ' || cur_r.col1 || ' = ''SCOTT''' ||
14 ' AND ' || cur_r.col2 || ' = date ''1982-12-09''';
15
16 EXECUTE IMMEDIATE (l_str) INTO l_cnt;
17
18 IF l_cnt > 0 THEN
19 dbms_output.put_line(l_cnt ||' row(s) in ' || cur_r.table_name);
20 END IF;
21 END LOOP;
22 END;
23 /
1 row(s) in EMP
PL/SQL procedure successfully completed.
SQL>

Is it possible to pass a table name to a SQL query by select query in oracle

Is it possible to pass a table name to a query using a result of another query?
SELECT T.MID, T.TID, M.NAME
FROM 'ONLINETRANSACTION#(' || SELECT ONLINEDBLINK FROM PARAMETERTABLE ||')' T
LEFT JOIN 'ONLINEMERCHANT#(' || SELECT ONLINEDBLINK FROM PARAMETERTABLE ||')' M
ON T.MID = M.MID
I have tried with the above code but it doesn't work.
This is a simple example based on Scott's schema.
lines 5 - 7 select your "table name" (actually, it appears that it is a database link name in your code. Doesn't matter, the principle is just the same)
line 9 uses that "table name" and concatenates it with the rest of the SELECT statement; finally, it executes it using EXECUTE IMMEDIATE
SQL> create table param (table_name varchar2(30));
Table created.
SQL> insert into param values ('EMP');
1 row created.
SQL> set serveroutput on
SQL> declare
2 l_table_name param.table_name%type;
3 l_max_sal emp.sal%type;
4 begin
5 select table_name into l_table_name
6 from param
7 where rownum = 1;
8
9 execute immediate 'select max(sal) from ' || l_table_name into l_max_sal;
10 dbms_output.put_line('Max salary = ' || l_max_sal);
11 end;
12 /
Max salary = 10000
PL/SQL procedure successfully completed.
SQL>

Dynamic sql/query: apostrophe in SELECT

So I have the problem, that there is a variable, which could contains different column names
and then in the SELECT I want to compare the column with a specific word.
But then it seems like the apostrophe make problems:
query := 'SELECT value FROM table WHERE ' || variable || ' like ''word''';
EXECUTE IMMEDIATE query INTO rec;
EXCEPTION WHEN OTHERS THEN htp.p(dbms_utility.format_error_stack);
SQL> set serveroutput on;
SQL> DECLARE
2 VAR VARCHAR2(20);
3 REC NUMBER;
4 query VARCHAR2(1000);
5 BEGIN
6 var := 'TABLE_NAME';
7 QUERY := 'SELECT count(*) FROM USER_TABLES WHERE ' || VAR || ' like ''%EMP%''';
8 dbms_output.put_line(query);
9 EXECUTE IMMEDIATE QUERY INTO REC;
10 dbms_output.put_line(rec);
11 END;
12 /
SELECT count(*) FROM USER_TABLES WHERE TABLE_NAME like '%EMP%'
1
PL/SQL procedure successfully completed.
With REC as collection type :
SQL> DECLARE
2 var VARCHAR2(20);
3 TYPE rec_typ
4 IS TABLE OF user_tables%ROWTYPE;
5 rec REC_TYP;
6 query VARCHAR2(1000);
7 BEGIN
8 var := 'TABLE_NAME';
9
10 query := 'SELECT * FROM USER_TABLES WHERE '
11 || var
12 || ' like ''%EMP%''';
13
14 dbms_output.Put_line(query);
15
16 EXECUTE IMMEDIATE query bulk collect INTO rec;
17
18 FOR i IN 1..rec.count LOOP
19 dbms_output.Put_line(Rec(i).table_name);
20 END LOOP;
21 END;
22 /
SELECT * FROM USER_TABLES WHERE TABLE_NAME like '%EMP%'
EMP
PL/SQL procedure successfully completed.
If you're searching for values in column that are equal to word then change like to =.
If you're searching for values in column that contains word then change like ''word'' to like ''%word%'' (don't forget about the third apostrophe that's closing dynamic query).