I'm trying this code in PL/SQL developer but it's not executing nothing. How can I use this code in order to find all tables which column values contains a specific value?
declare
num_rows number;
sql_text varchar2(250);
sql_info varchar2(100);
begin
dbms_output.enable(1000000);
for x in (select table_name, column_name from all_tab_columns
where data_type in ('VARCHAR','VARCHAR2','CHAR')
and owner='SYSTEM')
loop
sql_text:='select count(*) into :num_rows from SYSTEM.'||x.table_name||' where '||x.column_name||' like ''%10305698%''';
-- dbms_output.put_line (sql_text);
execute immediate sql_text into num_rows;
if num_rows>0
then
sql_info:='Table: '||x.table_name||' contains the string';
dbms_output.put_line (sql_info);
end if;
end loop;
end;
/
The below demonstration is to Search for a VALUE in all COLUMNS of all TABLES in an entire SCHEMA:
Search a CHARACTER type
Let's look for the value KING in SCOTT schema.
SQL> variable val varchar2(10)
SQL> exec :val := 'KING'
PL/SQL procedure successfully completed.
SQL> SELECT DISTINCT SUBSTR (:val, 1, 11) "Searchword",
2 SUBSTR (table_name, 1, 14) "Table",
3 SUBSTR (column_name, 1, 14) "Column"
4 FROM cols,
5 TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select '
6 || column_name
7 || ' from '
8 || table_name
9 || ' where upper('
10 || column_name
11 || ') like upper(''%'
12 || :val
13 || '%'')' ).extract ('ROWSET/ROW/*') ) ) t
14 ORDER BY "Table"
15 /
Searchword Table Column
----------- -------------- --------------
KING EMP ENAME
SQL>
Search a NUMERIC type
Let's look for the value 20 in SCOTT schema.
SQL> variable val NUMBER
SQL> exec :val := 20
PL/SQL procedure successfully completed.
SQL> SELECT DISTINCT SUBSTR (:val, 1, 11) "Searchword",
2 SUBSTR (table_name, 1, 14) "Table",
3 SUBSTR (column_name, 1, 14) "Column"
4 FROM cols,
5 TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select '
6 || column_name
7 || ' from '
8 || table_name
9 || ' where upper('
10 || column_name
11 || ') like upper(''%'
12 || :val
13 || '%'')' ).extract ('ROWSET/ROW/*') ) ) t
14 ORDER BY "Table"
15 /
Searchword Table Column
----------- -------------- --------------
20 DEPT DEPTNO
20 EMP DEPTNO
20 EMP HIREDATE
20 SALGRADE HISAL
20 SALGRADE LOSAL
SQL>
Related
I'm trying to get min and max values from query
SELECT TABLE_NAME , COLUMN_NAME
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME IN ('TABLE_A','TABLE_B')
and DATA_TYPE='NUMBER'
AND (DATA_PRECISION IS NULL OR DATA_SCALE IS NULL)
here what I get so far, but it shows nothing:
BEGIN DBMS_OUTPUT.ENABLE (buffer_size => NULL); END;
declare
l_max number;
begin
for "CUR_R" in
(SELECT TABLE_NAME , COLUMN_NAME
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME IN ('TABLE_A','TABLE_B')
and DATA_TYPE='NUMBER'
AND (DATA_PRECISION IS NULL OR DATA_SCALE IS NULL)
)
loop
execute immediate 'select max(' || "CUR_R"."COLUMN_NAME" ||') from ' || "CUR_R"."TABLE_NAME" into l_max;
dbms_output.put_line("CUR_R"."TABLE_NAME" ||'.'|| "CUR_R"."COLUMN_NAME" ||' -> max value = '|| l_max);
end loop;
end;
maybe i missing something?
also, I'm not an admin, just have grants to select to particular tables
can't create procedure or temp table
I expect result of this structure:
owner
column_name
max_value
min_value
maybe I am missing something?
also, I'm not an admin, just have grants to select to particular tables
can't create procedure or temp table
Dynamic SQL Approach
This query produces the query that gets the minand max for each column (line by line).
The simplest approach for ad Hoc purposed is to run the query, copy the result and execute it. You may of course fetch the result in a CLOBvariable and execute immediate it for repeatable tasks.
SELECT
'select '''||TABLE_NAME||''' TABLE_NAME ,''' || COLUMN_NAME||''' COLUMN_NAME,'|| ' max('|| COLUMN_NAME ||
') max_value,' || ' min('|| COLUMN_NAME ||') min_value from '|| TABLE_NAME ||
case when row_number() over (order by table_name desc, column_name desc) != 1 then ' UNION ALL' end as sql_text
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME IN ('TABLE_A','TABLE_B')
and DATA_TYPE='NUMBER'
AND (DATA_PRECISION IS NULL OR DATA_SCALE IS NULL)
order by table_name, column_name;
Result
select 'TABLE_A' TABLE_NAME ,'X' COLUMN_NAME, max(X) max_value, min(X) min_value from TABLE_A UNION ALL
select 'TABLE_A' TABLE_NAME ,'Y' COLUMN_NAME, max(Y) max_value, min(Y) min_value from TABLE_A UNION ALL
select 'TABLE_B' TABLE_NAME ,'X' COLUMN_NAME, max(X) max_value, min(X) min_value from TABLE_B UNION ALL
select 'TABLE_B' TABLE_NAME ,'Y' COLUMN_NAME, max(Y) max_value, min(Y) min_value from TABLE_B
I guess you should remove (comment) precision and scale conditions in cursor's query.
I used user_tab_columns and tables I have in my schema, but ... that's it, more or less:
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 l_max NUMBER;
3 BEGIN
4 FOR cur_r IN (SELECT table_name, column_name
5 FROM user_tab_columns
6 WHERE table_name IN ('EMP', 'DEPT')
7 AND data_type = 'NUMBER'
8 -- AND ( data_precision IS NULL
9 -- OR data_scale IS NULL)
10 AND 1 = 1)
11 LOOP
12 EXECUTE IMMEDIATE 'select max('
13 || cur_r.column_name
14 || ') from '
15 || cur_r.table_name
16 INTO l_max;
17
18 DBMS_OUTPUT.put_line (
19 cur_r.table_name
20 || '.'
21 || cur_r.column_name
22 || ' -> max value = '
23 || l_max);
24 END LOOP;
25 END;
26 /
DEPT.DEPTNO -> max value = 40
EMP.EMPNO -> max value = 7934
EMP.MGR -> max value = 7902
EMP.SAL -> max value = 5000
EMP.COMM -> max value = 1400
EMP.DEPTNO -> max value = 30
PL/SQL procedure successfully completed.
SQL>
Your query will only return column values where the data type is NUMBER or NUMBER(*,X). If the data type is NUMBER(X) or NUMBER(X,Y) then the filter on DATA_PRECISION IS NULL OR DATA_SCALE IS NULL will exclude those columns.
For example, given the sample data:
CREATE TABLE table_a (
value1 NUMBER,
value2 NUMBER(*,0),
value3 NUMBER(10),
value4 NUMBER(5,2)
);
INSERT INTO table_a
SELECT LEVEL, 2*LEVEL, 3*LEVEL, 4*LEVEL FROM DUAL CONNECT BY LEVEL <= 5;
CREATE TABLE table_b (
value1 NUMBER,
value2 NUMBER(*,0),
value3 NUMBER(10),
value4 NUMBER(5,2)
);
INSERT INTO table_b
SELECT LEVEL + 5, 2*LEVEL - 2, 3*LEVEL + 3, 1/LEVEL FROM DUAL CONNECT BY LEVEL <= 4;
Then:
DECLARE
l_min number;
l_max number;
BEGIN
DBMS_OUTPUT.ENABLE();
FOR c IN (
SELECT OWNER,
TABLE_NAME,
COLUMN_NAME
FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME IN ('TABLE_A','TABLE_B')
AND DATA_TYPE='NUMBER'
AND (DATA_PRECISION IS NULL OR DATA_SCALE IS NULL)
)
LOOP
EXECUTE IMMEDIATE
'select min("' || c.column_name ||'"),
max("' || c.column_name ||'")
from "' || c.owner || '"."' || c.table_name || '"'
INTO l_min, l_max;
DBMS_OUTPUT.PUT_LINE(
c.owner || '.' || c.table_name ||'.'|| c.column_name
|| ' -> min_value = ' || l_min
|| ', max value = '|| l_max
);
END LOOP;
END;
/
Outputs:
FIDDLE_TNHCDFVJDASVYWAHROPU.TABLE_A.VALUE1 -> min_value = 1, max value = 5
FIDDLE_TNHCDFVJDASVYWAHROPU.TABLE_A.VALUE2 -> min_value = 2, max value = 10
FIDDLE_TNHCDFVJDASVYWAHROPU.TABLE_B.VALUE1 -> min_value = 6, max value = 9
FIDDLE_TNHCDFVJDASVYWAHROPU.TABLE_B.VALUE2 -> min_value = 0, max value = 6
The columns value3 and value4 are excluded as they have both scale and precision specified in their data type.
fiddle
why not query like:
select max(x), min(x)
from t
I am looking towards unifying a number of columns across my data warehouse
My source system is Oracle based, and I would like to define my columns in my warehouse in such a way that a given column name can only have one data type and length
I am looking towards getting info on actual length used in the columns in my source system, and can identity the column name, datatype and length through this script
SELECT DISTINCT
column_name,
data_type,
data_length
FROM
all_tab_columns
ORDER BY
column_name
This does not however result in actual MAX(LENGTH()) of the individual columns
Is this possible to obtain, perhaps through a loop function?
Something like this, perhaps? Just an idea, modify it as you wish.
SQL> set serveroutput on;
SQL> declare
2 l_str varchar2(500);
3 l_maxlen number;
4 begin
5 for cur_r in (select table_name, column_name, data_type
6 from user_tab_columns
7 where table_name in ('EMP', 'DEPT')
8 order by table_name, column_name
9 )
10 loop
11 l_str := 'select max(length(' || cur_r.column_name || '))' ||
12 ' from ' || cur_r.table_name;
13 execute immediate l_str into l_maxlen;
14
15 dbms_output.put_line(cur_r.table_name ||'.'|| cur_r.column_name ||
16 ' - ' || cur_r.data_type ||': '|| l_maxlen);
17 end loop;
18 end;
19 /
DEPT.DEPTNO - NUMBER: 2
DEPT.DNAME - VARCHAR2: 10
DEPT.LOC - VARCHAR2: 8
EMP.COMM - NUMBER: 4
EMP.DEPTNO - NUMBER: 2
EMP.EMPNO - NUMBER: 4
EMP.ENAME - VARCHAR2: 6
EMP.HIREDATE - DATE: 8
EMP.JOB - VARCHAR2: 9
EMP.MGR - NUMBER: 4
EMP.SAL - NUMBER: 4
PL/SQL procedure successfully completed.
SQL>
create or replace PROCEDURE COMPARAISON_TEST as
begin
declare
cursor c_tab is
select table_name, column_name , data_type
from all_tab_columns;
v_sql VARCHAR2 (32000);
begin
FOR r_tab in c_tab LOOP
v_sql := 'SELECT ' ||
r_tab.table_name || ' TABLE_NAME, ' ||
r_tab.column_name || ' COLUMN_NAME, ' ||
'(SELECT MAX(' || r_tab.column_name || ') FROM ' || r_tab.table_name || ') VALUE ' ||
'FROM DUAL ';
execute immediate v_sql;
end LOOP;
end;
end COMPARAISON_TEST;
ERRORS
ORA-00904: "DUAL" : invalid identifier
ORA-06512: à "ODS.COMPARAISON_TEST", line 20
ORA-06512: à "ODS.COMPARAISON_TEST", line 20
ORA-06512: à line 2
Problem is that you're doing it in the dark.
Always, before actually executing dynamic SQL, display it on the screen so that you'd see the string to be executed.
This is what you did (I added WHERE clause to a cursor, to shorten the output):
SQL> declare
2
3 cursor c_tab is
4 select table_name, column_name , data_type
5 from all_tab_columns
6 where owner = 'SCOTT' and table_name = 'DEPT';
7
8 v_sql VARCHAR2 (32000);
9
10 begin
11
12 FOR r_tab in c_tab LOOP
13 v_sql := 'SELECT ' ||
14 r_tab.table_name || ' TABLE_NAME, ' ||
15 r_tab.column_name || ' COLUMN_NAME, ' ||
16 '(SELECT MAX(' || r_tab.column_name || ') FROM ' || r_tab.table_name || ') VALUE ' ||
17 'FROM DUAL ';
18 dbms_output.put_line(v_sql);
19 --execute immediate v_sql;
20 end LOOP;
21 end;
22 /
SELECT DEPT TABLE_NAME, DEPTNO COLUMN_NAME, (SELECT MAX(DEPTNO) FROM DEPT) VALUE
FROM DUAL
SELECT DEPT TABLE_NAME, DNAME COLUMN_NAME, (SELECT MAX(DNAME) FROM DEPT) VALUE
FROM DUAL
SELECT DEPT TABLE_NAME, LOC COLUMN_NAME, (SELECT MAX(LOC) FROM DEPT) VALUE FROM
DUAL
PL/SQL procedure successfully completed.
SQL>
See all those invalid strings? They can't be executed. In order to make them valid, include table and column names into single quotes, e.g.
SELECT 'DEPT' TABLE_NAME, 'DEPTNO' COLUMN_NAME, (SELECT MAX(DEPTNO) FROM DEPT) VALUE
FROM DUAL;
This is what you might have wanted:
SQL> set serveroutput on
SQL>
SQL> declare
2 cursor c_tab is
3 select table_name, column_name , data_type
4 from user_tab_columns
5 where table_name = 'DEPT';
6 v_sql VARCHAR2 (32000);
7 v_val varchar2(200);
8 begin
9 FOR r_tab in c_tab LOOP
10 v_sql := 'SELECT MAX(' || r_tab.column_name || ') FROM ' ||
11 r_tab.table_name;
12 execute immediate v_sql into v_val;
13 dbms_output.put_line(r_tab.table_name||'.'||r_tab.column_name||': '|| v_val);
14 end LOOP;
15 end;
16 /
DEPT.DEPTNO: 40
DEPT.DNAME: SALES
DEPT.LOC: NEW YORK
PL/SQL procedure successfully completed.
SQL>
If you want to store result into a table, then you'd do something like this:
SQL> create table maxes
2 (table_name varchar2(30),
3 column_name varchar2(30),
4 max_value varchar2(30)
5 );
Table created.
SQL> declare
2 cursor c_tab is
3 select table_name, column_name , data_type
4 from user_tab_columns
5 where table_name = 'DEPT';
6 v_sql VARCHAR2 (32000);
7 v_val varchar2(200);
8 begin
9 FOR r_tab in c_tab LOOP
10 v_sql := 'SELECT MAX(' || r_tab.column_name || ') FROM ' ||
11 r_tab.table_name;
12 execute immediate v_sql into v_val;
13 insert into maxes (table_name, column_name, max_value)
14 values
15 (r_tab.table_name, r_tab.column_name, v_val);
16 end LOOP;
17 end;
18 /
PL/SQL procedure successfully completed.
SQL> select * From maxes;
TABLE_NAME COLUMN_NAME MAX_VALUE
--------------- --------------- ---------------
DEPT DEPTNO 40
DEPT DNAME SALES
DEPT LOC NEW YORK
SQL>
You should double single quotes that you want to appear within the string.
v_sql := 'SELECT ''' ||
r_tab.table_name || ''' TABLE_NAME, ''' ||
r_tab.column_name || ''' COLUMN_NAME, ' ||
'(SELECT MAX(' || r_tab.column_name || ') FROM ' || r_tab.table_name || ') VALUE ' ||
'FROM DUAL ';
How to fetch odd columns in Oracle using a query when number of columns and name of columns are not known?
E.g.:
I need to get output in below format
Column1 column3 column5 column7
And so on....
You need to use the dynamic queries in the procedure as follows:
SQL> CREATE OR REPLACE PROCEDURE ODD_COLUMNS (
2 TABLE_NAME_P IN VARCHAR2,
3 DATAA OUT SYS_REFCURSOR
4 ) AS
5 V_SQL VARCHAR2(4000);
6 BEGIN
7 SELECT
8 'SELECT '
9 ||
10 LISTAGG(COLUMN_NAME, ',') WITHIN GROUP(
11 ORDER BY
12 COLUMN_ID
13 )
14 || ' FROM "'
15 || TABLE_NAME_P
16 || '"'
17 INTO V_SQL
18 FROM
19 USER_TAB_COLS
20 WHERE
21 TABLE_NAME = TABLE_NAME_P
22 AND MOD(COLUMN_ID, 2) = 1;
23
24 OPEN DATAA FOR V_SQL;
25
26 END ODD_COLUMNS;
27 /
Procedure created.
SQL>
Now, Let's test it:
SQL> variable rc refcursor;
SQL> exec ODD_COLUMNS('EMP',:rc);
PL/SQL procedure successfully completed.
SQL> print rc;
EMP_ID E
---------- -
10 N
20 Y
SQL>
SQL> exec ODD_COLUMNS('MY_TABLE1',:rc);
PL/SQL procedure successfully completed.
SQL> print rc;
ID REQ_QTY
---------- ----------
1001 10
1001 20
1001 30
1002 40
1003 10
1003 20
6 rows selected.
SQL>
Cheers!!
This cannot be done simply, but it is possible using the Oracle data dictionary and some dynamic SQL.
To find out the odd-numbered columns you need to look at the ALL_TAB_COLUMNS view. Column COLUMN_ID sequences the columns 1,2,3. So this will find all the odd-numbered columns in the SCOTT.EMP table:
select column_name, column_id
from all_tab_columns
where owner = 'SCOTT'
and table_name = 'EMP'
and mod(column_id,2) = 1
order by column_id;
This will return something like:
COLUMN_NAME COLUMN_ID
----------- ---------
EMPNO 1
JOB 3
HIREDATE 5
COMM 7
We can use the LISTAGG function to make that into a comma-separated list:
select listagg(column_name,',') within group (order by column_id) as result
from user_tab_columns
where table_name = 'EMP'
and mod(column_id,2) = 1;
RESULT
------
EMPNO,JOB,HIREDATE,COMM
Now we can add to that SQL to generate the select statement you want:
select 'select ' || listagg(column_name,',') within group (order by column_id) || ' from ' || table_name as sql
from user_tab_columns
where table_name = 'EMP'
and mod(column_id,2) = 1
group by table_name;
SQL
---
select EMPNO,JOB,HIREDATE,COMM from EMP
(Note I had to add a group by clause because table_name is not being aggregated by LISTAGG).
You could use that SQL within some PL/SQL code to populate a variable v_sql, then use the DBMS_SQL package to run it. But that is a complex topic in itself and I won't go into it here.
Example scenario:
5 tables are there and one common field among them is com_field (DATE data type). Now, I need to find the maximum of com_field in each of the five tables. Can someone give the logic?
I know UNION could be used but I need the flexibility not to miss any new table added to the OWNER.
The result I am expecting is like the below.
Table Max(com_field)
Tbl1 10/21/2019
Tbl2 10/18/2019
Tbl3 10/28/2019
Tbl4 09/30/2019
Tbl5 09/09/2019
Run this query:
SELECT
'SELECT '''||OWNER||'.'||TABLE_NAME ||''' AS TABLE_NAME , '||'MAX(COM_FIELD)AS COM_FIELD FROM ' ||OWNER||'.'||TABLE_NAME ||' UNION ALL'
FROM ALL_TAB_COLUMNS
WHERE COLUMN_NAME ='COM_FIELD'
then copy the outpu and delete last union all keyword. Then run the sql statement.
You can order it and see the max value
One option is to use dynamic SQL in a function that returns refcursor. Here's an example.
First, test case:
SQL> create table taba (com_field date);
Table created.
SQL> create table tabb (com_field date);
Table created.
SQL> create table tabc (com_field date);
Table created.
SQL> insert all
2 into taba values (sysdate)
3 into taba values (sysdate - 2)
4 into taba values (sysdate - 3)
5 into tabb values (sysdate + 2)
6 into tabc values (sysdate + 4)
7 into tabc values (sysdate + 5)
8 select * From dual;
6 rows created.
SQL>
Function:
SQL> create or replace function f_maxcom
2 return sys_refcursor
3 is
4 l_str varchar2(1000);
5 rc sys_refcursor;
6 begin
7 for cur_r in (select table_name
8 from user_tab_columns
9 where column_name = 'COM_FIELD'
10 )
11 loop
12 l_str := l_str ||
13 'select ' || chr(39) || cur_r.table_name || chr(39) || ', ' ||
14 'max(com_field) from ' || cur_r.table_name || ' union all ';
15 end loop;
16
17 l_str := rtrim(l_str, ' union all');
18
19 open rc for l_str;
20 return rc;
21 end;
22 /
Function created.
SQL>
Let's try it:
SQL> select f_maxcom from dual;
F_MAXCOM
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
'TAB MAX(COM_FI
---- ----------
TABA 29.10.2019
TABB 31.10.2019
TABC 03.11.2019
SQL>
Add another table to see what happens; function will stay as is:
SQL> create table littlefoot (id number, com_field date);
Table created.
SQL> insert into littlefoot values (100, sysdate);
1 row created.
SQL> select f_maxcom from dual;
F_MAXCOM
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
'LITTLEFOO MAX(COM_FI
---------- ----------
LITTLEFOOT 29.10.2019
TABA 29.10.2019
TABB 31.10.2019
TABC 03.11.2019
SQL>
Seems to be OK, eh?