SQL to Search for a particular VALUE in all COLUMNS of all TABLES in an entire SCHEMA in Oracle DB [duplicate] - sql

This question already has answers here:
Search All Fields In All Tables For A Specific Value (Oracle)
(17 answers)
Closed 8 months ago.
Is there SQL to Search for a particular VALUE in all COLUMNS of all TABLES in an entire SCHEMA in Oracle DB. Please explain.

You'll need dynamic SQL, as you have to compose select statement.
Here's an example; adjust it according to your needs because datatypes matter (e.g. you can't compare numbers to dates and such; I'm handling it simply by skipping over errors):
SQL> declare
2 l_search_value varchar2(20) := '10';
3 l_str varchar2(500);
4 l_cnt number;
5 begin
6 for cur_r in (select table_name, column_name, data_type
7 from user_tab_columns
8 )
9 loop
10 begin
11 l_str := 'select count(*) from ' || cur_r.table_name ||
12 ' where ' || cur_r.column_name || ' = ' || l_search_value;
13 execute immediate l_str into l_cnt;
14
15 if l_cnt > 0 then
16 dbms_output.put_line(rpad(cur_r.table_name ||'.'||cur_r.column_name, 30, ' ') ||
17 ' found ' || l_cnt || ' time(s)');
18 end if;
19
20 exception
21 when others then
22 --dbms_output.put_line(rpad(cur_r.table_name ||'.'||cur_r.column_name, 30, ' ')
23 -- ||' - ' || cur_r.data_type ||'; '|| sqlerrm);
24 null;
25 end;
26 end loop;
27 end;
28 /
DEPARTMENTS.DEPARTMENT_ID found 1 time(s)
DEPT.DEPTNO found 1 time(s)
EMP.DEPTNO found 3 time(s)
EMPLOYEES.DEPARTMENT_ID found 3 time(s)
PL/SQL procedure successfully completed.
SQL>

Related

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>

ORACLE - Count specific value from whole table (all columns)

I've got a table named "F_ParqueInfra", that I'd like to count all values in it where the value is equal to -1.
So, this table has 11 columns and 833 rows = 9.163 number of data in this table.
I'd like to know, how many "-1" values has in the whole table (all columns), in the simplest way.
Also I'll do that with a lot of tables in my Data Warehouse.
Really thanks!
One option is to use dynamic SQL. For example:
SQL> select * from f_parqueinfra;
ID_USUARIO ID_EMPRESA ID_DEPARTAMENTO
---------- ---------- ---------------
250 32 12
-1 -1 -1
0 -1 1
5 2 -1
SQL> set serveroutput on;
SQL> declare
2 l_table_name varchar2(30) := 'F_PARQUEINFRA';
3 l_value number := -1; -- search value
4 l_str varchar2(200); -- to compose SELECT statement
5 l_cnt number := 0; -- number of values in one column
6 l_sum number := 0; -- total sum of values
7 begin
8 for cur_r in (select table_name, column_name
9 from user_tab_columns
10 where table_name = l_table_name
11 and data_type = 'NUMBER'
12 )
13 loop
14 l_str := 'select count(*) from ' ||cur_r.table_name ||
15 ' where ' || cur_r.column_name || ' = ' || l_value;
16 execute immediate l_str into l_cnt;
17 l_sum := l_sum + l_cnt;
18 end loop;
19 dbms_output.put_line('Number of ' || l_value ||' values = ' || l_sum);
20 end;
21 /
Number of -1 values = 5
PL/SQL procedure successfully completed.
SQL>
If you change l_value (line #3), you can search for some other value, e.g.
SQL> l3
3* l_value number := -1; -- search value
SQL> c/-1/250
3* l_value number := 250; -- search value
SQL> /
Number of 250 values = 1
PL/SQL procedure successfully completed.
SQL>
Or, you can change table name (line #2) and search some other table.
Basically, you'd probably want to turn that anonymous PL/SQL code into a function which would accept table name and search value and return number of appearances. That shouldn't be too difficult so I'll leave it to you, for practice.
[EDIT: converting it into a function]
Although far from being perfect, something like this will let you search for some numeric and string values in tables in current schema. Dates are more complex, depending on formats etc. but - for simple cases - this code might be OK for you. Have a look:
SQL> create or replace function f_cnt (par_table_name in varchar2,
2 par_data_type in varchar2,
3 par_value in varchar2)
4 return sys.odcivarchar2list
5 is
6 l_data_type varchar2(20) := upper(par_data_type);
7 l_retval sys.odcivarchar2list := sys.odcivarchar2list();
8 l_str varchar2(200); -- to compose SELECT statement
9 l_out varchar2(200); -- return value
10 l_cnt number := 0; -- number of values in one column
11 l_sum number := 0; -- total sum of values
12 begin
13 -- loop through all tables in current schema
14 for cur_t in (select table_name
15 from user_tables
16 where table_name = upper(par_table_name)
17 or par_table_name is null
18 )
19 loop
20 -- reset the counter
21 l_sum := 0;
22 -- loop through all columns in that table
23 for cur_c in (select column_name
24 from user_tab_columns
25 where table_name = cur_t.table_name
26 and data_type = l_data_type
27 )
28 loop
29 -- pay attention to search value's data type
30 if l_data_type = 'VARCHAR2' then
31 l_str := 'select count(*) from ' ||cur_t.table_name ||
32 ' where ' || cur_c.column_name || ' = ' ||
33 chr(39) || par_value ||chr(39);
34 elsif l_data_type = 'NUMBER' then
35 l_str := 'select count(*) from ' ||cur_t.table_name ||
36 ' where ' || cur_c.column_name || ' = ' || par_value;
37 end if;
38
39 execute immediate l_str into l_cnt;
40 l_sum := l_sum + l_cnt;
41 end loop;
42
43 if l_sum > 0 then
44 l_out := cur_t.table_name ||' has ' || l_sum ||' search values';
45 l_retval.extend;
46 l_retval(l_retval.count) := l_out;
47 end if;
48 end loop;
49 return l_retval;
50 end;
51 /
Function created.
Testing:
SQL> select * From table(f_cnt(null, 'number', -1));
COLUMN_VALUE
-----------------------------------------------------------------
F_PARQUEINFRA has 5 search values
SQL> select * From table(f_cnt(null, 'varchar2', 'KING'));
COLUMN_VALUE
-----------------------------------------------------------------
EMP has 1 search values
SQL>
This might be a good place to use the unpivot syntax. This still requires you to type all the column names once - but not more.
Here is an example for 4 columns:
select count(*) cnt
from mytable
unpivot(myval for mycol in (col1, col2, col3, col4))
where myval = -1
As a bonus, you can easily modify the query to get the number of -1 per column:
select mycol, count(*) cnt
from mytable
unpivot(myval for mycol in (col1, col2, col3, col4))
where myval = -1
group by mycol
This should give you what you need.
Notes:
performs true numeric comparison (for example would match -1.00 also) using an inline function to prevent error should the value in a compared cell be non-numeric. (if all your compared values are guaranteed to be numeric the inline function can be simplified dramatically)
searches only varchar2 and number column types (this can be changed if desired).
The code follows:
set serveroutput on size 10000
declare
vMyTableName varchar2(200) := 'F_ParqueInfra';
vMyValue number := -1;
vSQL varchar2(4000);
vTotal pls_integer;
vGrandTotal number(18) := 0;
cursor c1 is
select *
from user_tab_columns
where table_name = vMyTableName;
vInlineFn varchar2(4000) := 'with
function matchesMyValue(value varchar2) return pls_integer
is
begin
return case
when to_number(value) = '||vMyValue||' then
1
else
0
end;
exception
when value_error then
return 0;
end;
';
begin
for x in c1 loop
if x.data_type in ('VARCHAR2','NUMBER') then -- only looking in these column data types for -1 but you can flex this to suit your column type definitions
vSQL := 'select sum(matchesMyValue('||x.column_name||')) from '||vMyTableName;
execute immediate vInlineFn||vSQL into vTotal;
vGrandTotal := vGrandTotal + vTotal;
end if;
end loop;
dbms_output.put_line('Total cells containing -1 = '||vGrandTotal);
end;
/

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

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>

Getting ORA-00904 invalid identifier with pl/sql loop

When attempting to use a list of tables as segment names for a query against dba_segments I get an ORA-00904: invalid identifier error.
I've tried moving around various quotes in case it's a syntax error, but I'm not sure what the issue might be.
declare
v_sql_c1 varchar2 (1000);
V_dblink varchar2(100) := 'DB1';
begin
for c1 in (select * from TABLE_LIST)
loop
execute immediate' select /*+parallel*/ bytes from dba_extents '|| '#' ||V_dblink ||' a '
||' where segment_name ='||
c1.table_name
into v_sql_c1;
dbms_output.put_line(v_sql_c1);
end loop;
end;
/
I would like ideally for this to report a value for 'bytes' in every row in the table_name column of table_list, which is the same as the segment_name column of dba_segments.
Can anyone help?
This is what you currently have:
SQL> CREATE TABLE table_list (table_name VARCHAR2 (20));
Table created.
SQL> INSERT INTO table_list VALUES ('EMP');
1 row created.
SQL> set serveroutput on;
SQL> DECLARE
2 v_sql_c1 VARCHAR2 (1000);
3 V_dblink VARCHAR2 (100) := 'DB1';
4 v_sql VARCHAR2 (1000);
5 BEGIN
6 FOR c1 IN (SELECT * FROM TABLE_LIST)
7 LOOP
8 v_sql :=
9 ' select /*+parallel*/ bytes from dba_extents '
10 || '#'
11 || V_dblink
12 || ' a '
13 || ' where segment_name ='
14 || c1.table_name;
15
16 DBMS_OUTPUT.put_line (v_sql);
17
18 -- EXECUTE IMMEDIATE v_sql INTO v_sql_c1;
19
20 DBMS_OUTPUT.put_line (v_sql_c1);
21 END LOOP;
22 END;
23 /
select /*+parallel*/ bytes from dba_extents #DB1 a where segment_name =EMP
PL/SQL procedure successfully completed.
SQL>
See? An invalid SELECT statement.
But, if you
remove space in front of a database link name
apply single quotes to segment_name
you'll get something that might work:
SQL> DECLARE
2 v_sql_c1 VARCHAR2 (1000);
3 V_dblink VARCHAR2 (100) := 'DB1';
4 v_sql VARCHAR2 (1000);
5 BEGIN
6 FOR c1 IN (SELECT * FROM TABLE_LIST)
7 LOOP
8 v_sql :=
9 ' select /*+parallel*/ bytes from dba_extents'
10 || '#'
11 || V_dblink
12 || ' a '
13 || ' where segment_name ='
14 || CHR (39)
15 || c1.table_name
16 || CHR (39);
17
18 DBMS_OUTPUT.put_line (v_sql);
19
20 -- EXECUTE IMMEDIATE v_sql INTO v_sql_c1;
21
22 DBMS_OUTPUT.put_line (v_sql_c1);
23 END LOOP;
24 END;
25 /
select /*+parallel*/ bytes from dba_extents#DB1 a where segment_name ='EMP'
PL/SQL procedure successfully completed.
SQL>
Basically, you should always display statement you'll be running as a dynamic SQL, make sure it is correct, and then actually EXECUTE IMMEDIATE it.