ORA-00904 and ORA-06512 when I try to MAX all columns - oracle

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 ';

Related

Can we use collection variable as a table in execute immediate?

Declare
Type t_approved_node is record( node_rowid Hr
node_rowid%type, Node_+type hr.node_type%type);
Type t_val is table of t_approved_node Index by pls_integer;
V_node t_val;
V_tab varchar2(20);
V_col varchar2(400);
V_nrf_flg hr.hr_flag%type;
V_ubrf_flg hr.hr_flag%type := 3;
V_col_str varchar2(4000);
Begin
Begin
Select hr_flag into v_nrf_flg from hr;
End;
Begin
Select h.node_rowid, h.node_type bulk collect into v_node
from hr h, hr_attr_wfm haw
Where h.hr_relation_id = haw.uc_hr_relation_id
And h.node_type = 'UBR';
Begin
V_tab := 'UC_UBR';
Select listagg(column_name, ',' within group(order by
column_id)
Into v_col from user_tab_columns where table_name = v_tab;
End;
V_col_str := regex_replace( v_col, 'HR_FLAG', v_ubrf_flg);
Execute immediate ' insert into ' || v_tab || '( ' ||
V_col || ') ' || ' select '|| v_col_str || ' from ' ||
V_tab || 'R ' || q' [ where node_type = ' UBR' a
and hr_flag =:1 and exists( ] ' || ' select 1 ' || ' from table( ' ||
v_node || ')y' || q' [ where y.node_rowid = R.node_rowid ] )'
Using v_nrf_flag;
End;
End;
I was trying to execute above block getting below error.
Wrong number or types of arguments in call to ||
Final query should be like
insert into UC_UBR ( v_col)/*3 columns into v_col variable*/
select v_col_str /* 3 columns in v_col_str variable*/ from UC_UBR R where hr_flag =:1
and exists
(select 1 from table(v_node) /*collection variable*/ y
where y.node_rowid = r.node_rowod;
Can anyone help on this?
Your sample code is full of errors and does not make any sense at all. But if I focus on your question, then the answer is "yes". See this example:
CREATE OR REPLACE TYPE t_app AS OBJECT( nodeid NUMBER, Nodetype VARCHAR2(100));
CREATE OR REPLACE TYPE t_val IS TABLE OF t_app;
DECLARE
V_node t_val;
V_result t_val;
V_app t_app;
V_count NUMBER;
Sql_stmt VARCHAR2(100);
nodeid NUMBER;
Nodetype VARCHAR2(100);
BEGIN
SELECT t_app(nodeid, Nodetype) BULK COLLECT INTO V_node FROM HR;
Sql_stmt := 'SELECT count(*) FROM TABLE(:t)';
EXECUTE IMMEDIATE Sql_stmt INTO V_count USING V_node;
DBMS_OUTPUT.PUT_LINE ( 'V_count = ' || V_count );
Sql_stmt := 'SELECT nodeid, Nodetype FROM TABLE(:t) WHERE ROWNUM = 1';
EXECUTE IMMEDIATE Sql_stmt INTO nodeid, Nodetype USING V_node;
DBMS_OUTPUT.PUT_LINE ( 'nodeid = ' || nodeid );
DBMS_OUTPUT.PUT_LINE ( 'Nodetype = ' || Nodetype );
Sql_stmt := 'SELECT t_app(nodeid, Nodetype) FROM TABLE(:t) WHERE ROWNUM = 1';
EXECUTE IMMEDIATE Sql_stmt INTO V_app USING V_node;
DBMS_OUTPUT.PUT_LINE ( 'V_app = ' || XMLTYPE(V_app).getClobVal() );
Sql_stmt := 'SELECT t_app(nodeid, Nodetype) FROM TABLE(:t)';
EXECUTE IMMEDIATE Sql_stmt BULK COLLECT INTO V_result USING V_node;
END;
It would help if you posted real code you used, because this is full of syntax errors (missing sql_stmt local variable declaration, put.line (?)).
I have no idea what you plan to do with such a select statement as you can't execute it, it doesn't make any sense but - here you go; see line #20.
SQL> set serveroutput on
SQL>
SQL> DECLARE
2 TYPE t_app IS RECORD
3 (
4 nodeid NUMBER,
5 Nodetype VARCHAR2 (20)
6 );
7
8 TYPE t_val IS TABLE OF t_app
9 INDEX BY PLS_INTEGER;
10
11 V_node t_val;
12 V_tab VARCHAR2 (20);
13
14 sql_stmt VARCHAR2 (200);
15 BEGIN
16 SELECT empno, ename
17 BULK COLLECT INTO v_node
18 FROM emp;
19
20 Sql_stmt := 'select 1 from (' || v_node (1).nodeid || 'Y)';
21
22 DBMS_OUTPUT.put_line (sql_stmt);
23 END;
24 /
select 1 from (7369Y)
PL/SQL procedure successfully completed.
SQL>

Can I use this code in oracle pl/sql developer

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>

Adding conditionals to PL/SQL cursors

In the below PL/SQL program I have 2 cursors that are almost similar in functionality. I'm not sure, how I can retain the same functionality just by having one cursor and get rid of the get_all_employes cursor which doesn't have the where clause.
Thanks in advance for your time and help!
CREATE OR REPLACE PROCEDURE EMP_DETAILS(dept_id VARCHAR2) IS
CURSOR get_all_employes IS
SELECT EMP_ID, EMP_NAME, EMP_SAL, EMP_DEPT FROM EMP;
CURSOR get_employee_info IS
SELECT EMP_ID, EMP_NAME, EMP_SAL, EMP_DEPT FROM EMP WHERE EMP_DEPT = dept_id;
BEGIN
IF dept_id IS NULL THEN
FOR X IN get_all_employes;
DBMS_OUTPUT.PUT_LINE(X.EMP_ID || ' ' || X.EMP_NAME || ' ' || X.EMP_SAL || X.EMP_DEPT);
END LOOP;
ELSE
FOR Y IN get_all_employes;
DBMS_OUTPUT.PUT_LINE(Y.EMP_ID || ' ' || Y.EMP_NAME || ' ' || Y.EMP_SAL || Y.EMP_DEPT);
END LOOP;
END IF;
END EMP_DETAILS;
/
Use one cursor and include WHERE EMP_DEPT = dept_id OR dept_id IS NULL:
CREATE PROCEDURE EMP_DETAILS(
dept_id IN EMP.EMP_DEPT%TYPE
)
IS
CURSOR get_employee_info IS
SELECT EMP_ID,
EMP_NAME,
EMP_SAL,
EMP_DEPT
FROM EMP
WHERE EMP_DEPT = dept_id
OR dept_id IS NULL;
BEGIN
FOR Y IN get_employee_info LOOP
DBMS_OUTPUT.PUT_LINE(Y.EMP_ID || ' ' || Y.EMP_NAME || ' ' || Y.EMP_SAL || Y.EMP_DEPT);
END LOOP;
END EMP_DETAILS;
/
So, for the test data:
CREATE TABLE emp ( EMP_ID, EMP_NAME, EMP_SAL, EMP_DEPT ) AS
SELECT 1, 'a', 100, 1 FROM DUAL UNION ALL
SELECT 2, 'b', 200, 1 FROM DUAL UNION ALL
SELECT 3, 'c', 300, 2 FROM DUAL;
Then:
BEGIN
emp_details( NULL );
END;
/
Would output all the rows:
1 a 1001
2 b 2001
3 c 3002
and:
BEGIN
emp_details( 1 );
END;
/
would output only the rows for department 1:
1 a 1001
2 b 2001
db<>fiddle here
Try something like this (fyi your example code does not compile)
create or replace PROCEDURE EMP_DETAILS(dept_id VARCHAR2) IS
TYPE EmpCurTyp IS REF CURSOR;
emp_cv EmpCurTyp;
empRec EMP%ROWTYPE;
BEGIN
IF dept_id IS NULL THEN
open emp_cv for SELECT EMP_ID, EMP_NAME, EMP_SAL, EMP_DEPT FROM EMP;
ELSE
open emp_cv for SELECT EMP_ID, EMP_NAME, EMP_SAL, EMP_DEPT FROM EMP WHERE EMP_DEPT = dept_id;
END IF;
LOOP
FETCH emp_cv INTO empRec;
IF emp_cv%NOTFOUND
THEN
EXIT;
END IF;
DBMS_OUTPUT.PUT_LINE(empREc.EMP_ID || ' ' || empREc.EMP_NAME || ' ' || empREc.EMP_SAL || empREc.EMP_DEPT);
END LOOP;
CLOSE emp_cv;
END EMP_DETAILS;

Dynamic SQL Hangs [duplicate]

Is it possible to search every field of every table for a particular value in Oracle?
There are hundreds of tables with thousands of rows in some tables so I know this could take a very long time to query. But the only thing I know is that a value for the field I would like to query against is 1/22/2008P09RR8.
<
I've tried using this statement below to find an appropriate column based on what I think it should be named but it returned no results.
SELECT * from dba_objects
WHERE object_name like '%DTN%'
There is absolutely no documentation on this database and I have no idea where this field is being pulled from.
Any thoughts?
Quote:
I've tried using this statement below
to find an appropriate column based on
what I think it should be named but it
returned no results.*
SELECT * from dba_objects WHERE
object_name like '%DTN%'
A column isn't an object. If you mean that you expect the column name to be like '%DTN%', the query you want is:
SELECT owner, table_name, column_name FROM all_tab_columns WHERE column_name LIKE '%DTN%';
But if the 'DTN' string is just a guess on your part, that probably won't help.
By the way, how certain are you that '1/22/2008P09RR8' is a value selected directly from a single column? If you don't know at all where it is coming from, it could be a concatenation of several columns, or the result of some function, or a value sitting in a nested table object. So you might be on a wild goose chase trying to check every column for that value. Can you not start with whatever client application is displaying this value and try to figure out what query it is using to obtain it?
Anyway, diciu's answer gives one method of generating SQL queries to check every column of every table for the value. You can also do similar stuff entirely in one SQL session using a PL/SQL block and dynamic SQL. Here's some hastily-written code for that:
SET SERVEROUTPUT ON SIZE 100000
DECLARE
match_count INTEGER;
BEGIN
FOR t IN (SELECT owner, table_name, column_name
FROM all_tab_columns
WHERE owner <> 'SYS' and data_type LIKE '%CHAR%') LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM ' || t.owner || '.' || t.table_name ||
' WHERE '||t.column_name||' = :1'
INTO match_count
USING '1/22/2008P09RR8';
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
/
There are some ways you could make it more efficient too.
In this case, given the value you are looking for, you can clearly eliminate any column that is of NUMBER or DATE type, which would reduce the number of queries. Maybe even restrict it to columns where type is like '%CHAR%'.
Instead of one query per column, you could build one query per table like this:
SELECT * FROM table1
WHERE column1 = 'value'
OR column2 = 'value'
OR column3 = 'value'
...
;
I did some modification to the above code to make it work faster if you are searching in only one owner.
You just have to change the 3 variables v_owner, v_data_type and v_search_string to fit what you are searching for.
SET SERVEROUTPUT ON SIZE 100000
DECLARE
match_count INTEGER;
-- Type the owner of the tables you are looking at
v_owner VARCHAR2(255) :='ENTER_USERNAME_HERE';
-- Type the data type you are look at (in CAPITAL)
-- VARCHAR2, NUMBER, etc.
v_data_type VARCHAR2(255) :='VARCHAR2';
-- Type the string you are looking at
v_search_string VARCHAR2(4000) :='string to search here...';
BEGIN
FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and data_type = v_data_type) LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '||t.table_name||' WHERE '||t.column_name||' = :1'
INTO match_count
USING v_search_string;
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
/
I know this is an old topic. But I see a comment to the question asking if it could be done in SQL rather than using PL/SQL. So thought to post a solution.
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>
Yes you can and your DBA will hate you and will find you to nail your shoes to the floor because that will cause lots of I/O and bring the database performance really down as the cache purges.
select column_name from all_tab_columns c, user_all_tables u where c.table_name = u.table_name;
for a start.
I would start with the running queries, using the v$session and the v$sqlarea. This changes based on oracle version. This will narrow down the space and not hit everything.
Here is another modified version that will compare a lower substring match. This works in Oracle 11g.
DECLARE
match_count INTEGER;
-- Type the owner of the tables you are looking at
v_owner VARCHAR2(255) :='OWNER_NAME';
-- Type the data type you are look at (in CAPITAL)
-- VARCHAR2, NUMBER, etc.
v_data_type VARCHAR2(255) :='VARCHAR2';
-- Type the string you are looking at
v_search_string VARCHAR2(4000) :='%lower-search-sub-string%';
BEGIN
FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and data_type = v_data_type) LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '||t.table_name||' WHERE lower('||t.column_name||') like :1'
INTO match_count
USING v_search_string;
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
/
I modified Flood's script to execute once for each table rather than for every column of each table for faster execution. It requires Oracle 11g or greater.
set serveroutput on size 100000
declare
v_match_count integer;
v_counter integer;
-- The owner of the tables to search through (case-sensitive)
v_owner varchar2(255) := 'OWNER_NAME';
-- A string that is part of the data type(s) of the columns to search through (case-insensitive)
v_data_type varchar2(255) := 'CHAR';
-- The string to be searched for (case-insensitive)
v_search_string varchar2(4000) := 'FIND_ME';
-- Store the SQL to execute for each table in a CLOB to get around the 32767 byte max size for a VARCHAR2 in PL/SQL
v_sql clob := '';
begin
for cur_tables in (select owner, table_name from all_tables where owner = v_owner and table_name in
(select table_name from all_tab_columns where owner = all_tables.owner and data_type like '%' || upper(v_data_type) || '%')
order by table_name) loop
v_counter := 0;
v_sql := '';
for cur_columns in (select column_name from all_tab_columns where
owner = v_owner and table_name = cur_tables.table_name and data_type like '%' || upper(v_data_type) || '%') loop
if v_counter > 0 then
v_sql := v_sql || ' or ';
end if;
v_sql := v_sql || 'upper(' || cur_columns.column_name || ') like ''%' || upper(v_search_string) || '%''';
v_counter := v_counter + 1;
end loop;
v_sql := 'select count(*) from ' || cur_tables.table_name || ' where ' || v_sql;
execute immediate v_sql
into v_match_count;
if v_match_count > 0 then
dbms_output.put_line('Match in ' || cur_tables.owner || ': ' || cur_tables.table_name || ' - ' || v_match_count || ' records');
end if;
end loop;
exception
when others then
dbms_output.put_line('Error when executing the following: ' || dbms_lob.substr(v_sql, 32600));
end;
/
I was having following issues for #Lalit Kumars answer,
ORA-19202: Error occurred in XML processing
ORA-00904: "SUCCESS": invalid identifier
ORA-06512: at "SYS.DBMS_XMLGEN", line 288
ORA-06512: at line 1
19202. 00000 - "Error occurred in XML processing%s"
*Cause: An error occurred when processing the XML function
*Action: Check the given error message and fix the appropriate problem
Solution is:
WITH char_cols AS
(SELECT /*+materialize */ table_name, column_name
FROM cols
WHERE data_type IN ('CHAR', 'VARCHAR2'))
SELECT DISTINCT SUBSTR (:val, 1, 11) "Searchword",
SUBSTR (table_name, 1, 14) "Table",
SUBSTR (column_name, 1, 14) "Column"
FROM char_cols,
TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select "'
|| column_name
|| '" from "'
|| table_name
|| '" where upper("'
|| column_name
|| '") like upper(''%'
|| :val
|| '%'')' ).extract ('ROWSET/ROW/*') ) ) t
ORDER BY "Table"
/
I would do something like this (generates all the selects you need).
You can later on feed them to sqlplus:
echo "select table_name from user_tables;" | sqlplus -S user/pwd | grep -v "^--" | grep -v "TABLE_NAME" | grep "^[A-Z]" | while read sw;
do echo "desc $sw" | sqlplus -S user/pwd | grep -v "\-\-\-\-\-\-" | awk -F' ' '{print $1}' | while read nw;
do echo "select * from $sw where $nw='val'";
done;
done;
It yields:
select * from TBL1 where DESCRIPTION='val'
select * from TBL1 where ='val'
select * from TBL2 where Name='val'
select * from TBL2 where LNG_ID='val'
And what it does is - for each table_name from user_tables get each field (from desc) and create a select * from table where field equals 'val'.
if we know the table and colum names but want to find out the number of times string is appearing for each schema:
Declare
owner VARCHAR2(1000);
tbl VARCHAR2(1000);
cnt number;
ct number;
str_sql varchar2(1000);
reason varchar2(1000);
x varchar2(1000):='%string_to_be_searched%';
cursor csr is select owner,table_name
from all_tables where table_name ='table_name';
type rec1 is record (
ct VARCHAR2(1000));
type rec is record (
owner VARCHAR2(1000):='',
table_name VARCHAR2(1000):='');
rec2 rec;
rec3 rec1;
begin
for rec2 in csr loop
--str_sql:= 'select count(*) from '||rec.owner||'.'||rec.table_name||' where CTV_REMARKS like '||chr(39)||x||chr(39);
--dbms_output.put_line(str_sql);
--execute immediate str_sql
execute immediate 'select count(*) from '||rec2.owner||'.'||rec2.table_name||' where column_name like '||chr(39)||x||chr(39)
into rec3;
if rec3.ct <> 0 then
dbms_output.put_line(rec2.owner||','||rec3.ct);
else null;
end if;
end loop;
end;
Procedure to Search Entire Database:
CREATE or REPLACE PROCEDURE SEARCH_DB(SEARCH_STR IN VARCHAR2, TAB_COL_RECS OUT VARCHAR2) IS
match_count integer;
qry_str varchar2(1000);
CURSOR TAB_COL_CURSOR IS
SELECT TABLE_NAME,COLUMN_NAME,OWNER,DATA_TYPE FROM ALL_TAB_COLUMNS WHERE DATA_TYPE in ('NUMBER','VARCHAR2') AND OWNER='SCOTT';
BEGIN
FOR TAB_COL_REC IN TAB_COL_CURSOR
LOOP
qry_str := 'SELECT COUNT(*) FROM '||TAB_COL_REC.OWNER||'.'||TAB_COL_REC.TABLE_NAME||
' WHERE '||TAB_COL_REC.COLUMN_NAME;
IF TAB_COL_REC.DATA_TYPE = 'NUMBER' THEN
qry_str := qry_str||'='||SEARCH_STR;
ELSE
qry_str := qry_str||' like '||SEARCH_STR;
END IF;
--dbms_output.put_line( qry_str );
EXECUTE IMMEDIATE qry_str INTO match_count;
IF match_count > 0 THEN
dbms_output.put_line( qry_str );
--dbms_output.put_line( TAB_COL_REC.TABLE_NAME ||' '||TAB_COL_REC.COLUMN_NAME ||' '||match_count);
TAB_COL_RECS := TAB_COL_RECS||'##'||TAB_COL_REC.TABLE_NAME||'##'||TAB_COL_REC.COLUMN_NAME;
END IF;
END LOOP;
END SEARCH_DB;
Execute Statement
DECLARE
SEARCH_STR VARCHAR2(200);
TAB_COL_RECS VARCHAR2(200);
BEGIN
SEARCH_STR := 10;
SEARCH_DB(
SEARCH_STR => SEARCH_STR,
TAB_COL_RECS => TAB_COL_RECS
);
DBMS_OUTPUT.PUT_LINE('TAB_COL_RECS = ' || TAB_COL_RECS);
END;
Sample Results
Connecting to the database test.
SELECT COUNT(*) FROM SCOTT.EMP WHERE DEPTNO=10
SELECT COUNT(*) FROM SCOTT.DEPT WHERE DEPTNO=10
TAB_COL_RECS = ##EMP##DEPTNO##DEPT##DEPTNO
Process exited.
Disconnecting from the database test.
I don't of a simple solution on the SQL promprt. Howeve there are quite a few tools like toad and PL/SQL Developer that have a GUI where a user can input the string to be searched and it will return the table/procedure/object where this is found.
There are some free tools that make these kind of search, for example, this one works fine and source code is available:
https://sites.google.com/site/freejansoft/dbsearch
You'll need the Oracle ODBC driver and a DSN to use this tool.
Modifying the code to search case-insensitively using a LIKE query instead of finding exact matches...
DECLARE
match_count INTEGER;
-- Type the owner of the tables you want to search.
v_owner VARCHAR2(255) :='USER';
-- Type the data type you're looking for (in CAPS). Examples include: VARCHAR2, NUMBER, etc.
v_data_type VARCHAR2(255) :='VARCHAR2';
-- Type the string you are looking for.
v_search_string VARCHAR2(4000) :='Test';
BEGIN
dbms_output.put_line( 'Starting the search...' );
FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and data_type = v_data_type) LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '||t.table_name||' WHERE LOWER('||t.column_name||') LIKE :1'
INTO match_count
USING LOWER('%'||v_search_string||'%');
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
I found the best solution but it's a little slow. (It will work perfectly with all SQL IDE's.)
SELECT DISTINCT table_name, column_name, data_type
FROM user_tab_cols,
TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select '
|| column_name
|| ' from '
|| table_name
|| ' where lower('
|| column_name
|| ') like lower(''%'
|| 'your_text_here'
|| '%'')' ).extract ('ROWSET/ROW/*') ) ) a
where table_name not in (
select distinct table_name
from user_tab_cols where data_type like 'SDO%'
or data_type like '%LOB') AND DATA_TYPE = 'VARCHAR2'
order by table_name, column_name;
--it run completed -- no error
SET SERVEROUTPUT ON SIZE 100000
DECLARE
v_match_count INTEGER;
v_counter INTEGER;
v_owner VARCHAR2 (255) := 'VASOA';
v_search_string VARCHAR2 (4000) := '99999';
v_data_type VARCHAR2 (255) := 'CHAR';
v_sql CLOB := '';
BEGIN
FOR cur_tables
IN ( SELECT owner, table_name
FROM all_tables
WHERE owner = v_owner
AND table_name IN (SELECT table_name
FROM all_tab_columns
WHERE owner = all_tables.owner
AND data_type LIKE
'%'
|| UPPER (v_data_type)
|| '%')
ORDER BY table_name)
LOOP
v_counter := 0;
v_sql := '';
FOR cur_columns
IN (SELECT column_name, table_name
FROM all_tab_columns
WHERE owner = v_owner
AND table_name = cur_tables.table_name
AND data_type LIKE '%' || UPPER (v_data_type) || '%')
LOOP
IF v_counter > 0
THEN
v_sql := v_sql || ' or ';
END IF;
IF cur_columns.column_name is not null
THEN
v_sql :=
v_sql
|| 'upper('
|| cur_columns.column_name
|| ') ='''
|| UPPER (v_search_string)||'''';
v_counter := v_counter + 1;
END IF;
END LOOP;
IF v_sql is null
THEN
v_sql :=
'select count(*) from '
|| v_owner
|| '.'
|| cur_tables.table_name;
END IF;
IF v_sql is not null
THEN
v_sql :=
'select count(*) from '
|| v_owner
|| '.'
|| cur_tables.table_name
|| ' where '
|| v_sql;
END IF;
--v_sql := 'select count(*) from ' ||v_owner||'.'|| cur_tables.table_name ||' where '|| v_sql;
--dbms_output.put_line(v_sql);
--DBMS_OUTPUT.put_line (v_sql);
EXECUTE IMMEDIATE v_sql INTO v_match_count;
IF v_match_count > 0
THEN
DBMS_OUTPUT.put_line (v_sql);
dbms_output.put_line('Match in ' || cur_tables.owner || ': ' || cur_tables.table_name || ' - ' || v_match_count || ' records');
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Error when executing the following: '
|| DBMS_LOB.SUBSTR (v_sql, 32600));
END;
/
Borrowing, slightly enhancing and simplifying from this Blog post the following simple SQL statement seems to do the job quite well:
SELECT DISTINCT (:val) "Search Value", TABLE_NAME "Table", COLUMN_NAME "Column"
FROM cols,
TABLE (XMLSEQUENCE (DBMS_XMLGEN.GETXMLTYPE(
'SELECT "' || COLUMN_NAME || '" FROM "' || TABLE_NAME || '" WHERE UPPER("'
|| COLUMN_NAME || '") LIKE UPPER(''%' || :val || '%'')' ).EXTRACT ('ROWSET/ROW/*')))
ORDER BY "Table";
The Oracle LIKE condition allows wildcards to be used in the WHERE clause of a SELECT, INSERT, UPDATE, or DELETE statement.
%: to match any string of any length
Eg-
SELECT last_name
FROM customer_tab
WHERE last_name LIKE '%A%';
-: to match on a single character
Eg-
SELECT last_name
FROM customer_tab
WHERE last_name LIKE 'A_t';

pl/sql block code issue with multiple records return

can't make this work... have a multiple tables names which i need to pass into select. Each select will return multiple records. Resultset should be printed to the user on a screen .
SQL> set serveroutput on;
SQL> DECLARE
vs_statement VARCHAR2 (1000);
my_var1 VARCHAR2(100);
my_var2 VARCHAR2(100);
CURSOR c1 IS
SELECT table_name
FROM all_tables
WHERE table_name LIKE Upper('redit_1%');
BEGIN
FOR table_rec IN c1 LOOP
vs_statement :=
'select a.userinfo, a.userstatus into my_var1, my_var12 from '
|| table_rec.table_name
|| ' A, FILES b where A.objectid = B.id order by 1';
EXECUTE IMMEDIATE vs_statement INTO my_var1,
my_var2;
dbms_output.Put_line(my_var1
||' '
|| my_var2);
END LOOP;
END;
/
This line should look like the following (i.e. remove INTO clause):
vs_statement :=
'select a.userinfo, a.userstatus from '
|| table_rec.table_name
|| ' A, FILES b where A.objectid = B.id order by 1';
Also, make sure that this SELECT returns only 1 row, otherwise you'll end up with the TOO-MANY-ROWS error.
Other than that, I guess it should work.
[EDIT, regarding fear of TOO-MANY-ROWS]
Huh, I'd create a whole new PL/SQL BEGIN-END block, containing a loop which would do the job, displaying all rows returned by SELECT statement.
As I don't have your tables, I used HR's. Have a look:
SQL> DECLARE
2 vs_statement VARCHAR2 (1000);
3 my_var1 VARCHAR2(100);
4 my_var2 VARCHAR2(100);
5 CURSOR c1 IS
6 SELECT table_name
7 FROM all_tables
8 WHERE table_name LIKE Upper('%departments%');
9 BEGIN
10 FOR table_rec IN c1 LOOP
11 vs_statement := 'begin for cur_r in ( '
12 || Chr(10)
13 || 'select distinct a.department_id, a.department_name from '
14 || Chr(10)
15 || table_rec.table_name
16 ||
17 ' A, employees b where A.department_id = B.department_id order by 1) loop'
18 || Chr(10)
19 || ' dbms_output.put_line(cur_r.department_name); '
20 || Chr(10)
21 || ' end loop; end;';
22
23 EXECUTE IMMEDIATE vs_statement; -- into my_var1, my_var2;
24 END LOOP;
25 END;
26 /
Administration
Marketing
Purchasing
Human Resources
Shipping
IT
Public Relations
Sales
Executive
Finance
Accounting
PL/SQL procedure successfully completed.
SQL>
"Each select will return multiple records...Resultset should be printed to the user on a screen"
Open a ref cursor for each generated statement. Loop round that and display the values.
DECLARE
rc sys_refcursor;
vs_statement VARCHAR2 (1000);
my_var1 VARCHAR2(100);
my_var2 VARCHAR2(100);
CURSOR c1 IS
SELECT table_name
FROM all_tables
WHERE table_name LIKE Upper('redit_1%');
BEGIN
<< tab_loop >>
FOR table_rec IN c1 LOOP
vs_statement :=
'select a.userinfo, a.userstatus from '
|| table_rec.table_name
|| ' A, FILES b where A.objectid = B.id order by 1';
open rc for vs_statement;
dbms_output.put_line('records for '||table_rec.table_name);
<< rec_loop >>
loop
fetch rc into my_var1,my_var2;
exit when rc%notfound;
dbms_output.Put_line(my_var1
||' '
|| my_var2);
end loop rec_loop;
close rc;
END LOOP tab_loop;
END;
/

Resources