List aggregate column values when column contains customer - sql

I am running a query on ALL_TAB_COLUMNS to select for column_names where it contains the name customer, then enter these tables and LISTAGG the values from these columns.
However, I get an exceptionally long error that I cannot understand:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 10, column 1:
PL/SQL: SQL Statement ignored
ORA-06550: line 15, column 22:
PLS-00364: loop index variable 'TB' use is invalid
ORA-06550: line 15, column 1:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
Here is what I have tried:
DECLARE
vcol VARCHAR2(128);
vtable VARCHAR(128);
BEGIN
FOR VAL IN (SELECT COLUMN_NAME, TABLE_NAME FROM ALL_TAB_COLUMNS WHERE COLUMN_NAME LIKE '%Customer%' AND DATA_TYPE in ( 'CHAR' , 'VARCHAR2' ))
LOOP
vcol := VAL.COLUMN_NAME;
vtable := VAL.TABLE_NAME;
FOR TB IN (
WITH A AS (
SELECT DISTINCT vcol FROM vtable
) SELECT LISTAGG(vcol, ',') as cols FROM A
)
LOOP
dbms_output.put_line(TB.cols);
END LOOP;
END LOOP;
END;

You can't refer to table and column names using run-time variables, unless you use dynamic SQL. You could construct each statement as a string, then open the cursor dynamically using that query string:
DECLARE
vstmt VARCHAR2(4000);
vresult VARCHAR2(4000);
vcursor SYS_REFCURSOR;
BEGIN
FOR VAL IN (SELECT COLUMN_NAME, TABLE_NAME FROM ALL_TAB_COLUMNS WHERE COLUMN_NAME LIKE '%Customer%')
LOOP
vstmt := 'WITH A AS (
SELECT DISTINCT "' || VAL.COLUMN_NAME || '" FROM "' || VAL.TABLE_NAME || '"
) SELECT LISTAGG("' || VAL.COLUMN_NAME || '", '','') FROM A';
OPEN vcursor FOR vstmt;
LOOP
FETCH vcursor INTO vresult;
EXIT WHEN vcursor%NOTFOUND;
dbms_output.put_line(vresult);
END LOOP;
END LOOP;
END;
/
Or as the lack of a WITHIN GROUP clause on your LISTAGG implies you're on 19c or later, you can simplify the dynamic statement to:
vstmt := 'SELECT LISTAGG(DISTINCT "' || VAL.COLUMN_NAME || '", '','') FROM "' || VAL.TABLE_NAME || '"';
You can also do the same thing without PL/SQL, using an XML trick do handle the dynamic statement:
select table_name, column_name,
xmlquery('/ROWSET/ROW/C/text()'
passing xmltype(dbms_xmlgen.getxml(
'select listagg(distinct "' || column_name || '", '','') as c '
|| 'from "' || table_name || '"'))
returning content) as value_list
from all_tab_columns
where column_name like '%Customer%';
Your filter where column_name like '%Customer%' implies you have quoted identifiers. If that's not the case then the column name will be in uppercase by default; but you could search for any case with where upper(column_name) like '%CUSTOMER%'.
And as you're looking up the tables and columns from all_tables, not user_tables, you should really be specifying the owner as well, whether you stick with PL/SQL or use the XML approach:
select owner, table_name, column_name,
xmlquery('/ROWSET/ROW/C/text()'
passing xmltype(dbms_xmlgen.getxml(
'select listagg(distinct "' || column_name || '", '','') as c '
|| 'from "' || owner || '"."' || table_name || '"'))
returning content) as value_list
from all_tab_columns
where upper(column_name) like '%CUSTOMER%';
fiddle including some dummy tables.

Related

oracle: display results of dynamic sql based on antoher sql

I would like to display results of dynamic sql based on antoher sql, but get error message. My code:
DECLARE
sql_qry VARCHAR2(1000) := NULL;
TYPE results IS
TABLE OF all_tab_columns%rowtype;
results_tbl results;
BEGIN
FOR i IN (
SELECT
*
FROM
all_tab_columns
WHERE
owner = 'OWNER_XYZ'
AND upper(column_name) LIKE '%COLUMN_XYZ%'
ORDER BY
table_name,
column_name
) LOOP
sql_qry := ' SELECT DISTINCT '
|| i.column_name
|| ' as column_name '
|| ' FROM '
|| i.owner
|| '.'
|| i.table_name
|| ' WHERE SUBSTR('
|| i.column_name
|| ',1,1) = ''Y''';
EXECUTE IMMEDIATE sql_qry BULK COLLECT
INTO results_tbl;
dbms_output.put_line(results_tbl);
END LOOP;
END;
I get the error message:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
In fact I need the results of all queries with an union between them like that
[1] [1]: https://i.stack.imgur.com/llxzr.png
Your dynamic query is selecting a single column, but you are bulk collecting into results_tbl, which is of type results, based on all_tab_columns%rowtype - which has lots of columns.
If you define your collection type as a single column of the data type you need, i.e. some length of string:
DECLARE
sql_qry VARCHAR2(1000) := NULL;
TYPE results IS
TABLE OF varchar2(4000);
results_tbl results;
...
You will then bulk collect a single column into that single-column collection. To display the results you need to loop over the collection:
FOR j IN 1..results_tbl.COUNT loop
dbms_output.put_line(results_tbl(j));
END LOOP;
so the whole block becomes:
DECLARE
sql_qry VARCHAR2(1000) := NULL;
TYPE results IS
TABLE OF varchar2(4000);
results_tbl results;
BEGIN
FOR i IN (
SELECT
*
FROM
all_tab_columns
WHERE
owner = 'OWNER_XYZ'
AND upper(column_name) LIKE '%COLUMN_XYZ%'
ORDER BY
table_name,
column_name
) LOOP
sql_qry := ' SELECT DISTINCT '
|| i.column_name
|| ' as column_name '
|| ' FROM '
|| i.owner
|| '.'
|| i.table_name
|| ' WHERE SUBSTR('
|| i.column_name
|| ',1,1) = ''Y''';
EXECUTE IMMEDIATE sql_qry BULK COLLECT
INTO results_tbl;
FOR j IN 1..results_tbl.COUNT loop
dbms_output.put_line(results_tbl(j));
END LOOP;
END LOOP;
END;
/
However, you can also do this without PL/SQL, using a variation on an XML trick. You can get the results of the dynamic query as an XML document using something like:
select dbms_xmlgen.getxmltype(
'select distinct "' || column_name || '" as value'
|| ' from "' || owner || '"."' || table_name || '"'
|| ' where substr("' || column_name || '", 1, 1) = ''Y'''
)
from all_tab_columns
where owner = 'OWNER_XYZ'
and upper(column_name) like '%COLUMN_XYZ%';
and then extract the value you want from that:
with cte (xml) as (
select dbms_xmlgen.getxmltype(
'select distinct "' || column_name || '" as value'
|| ' from "' || owner || '"."' || table_name || '"'
|| ' where substr("' || column_name || '", 1, 1) = ''Y'''
)
from all_tab_columns
where owner = 'OWNER_XYZ'
and upper(column_name) like '%COLUMN_XYZ%'
)
select x.value
from cte
cross apply xmltable(
'/ROWSET/ROW'
passing cte.xml
columns value varchar2(4000) path 'VALUE'
) x;
You can also easily include the table each value came from if you want that information (and the owner, and actual column name, etc.).
db<>fiddle showing both approaches.

How to declare a number variable where I can save th count of table in my loop

I work wirh oracle Database. I have a plsql code where i run a query in a loop for multiple tables. so, table name is a variable in my code. I would like to have another variable (a single number) that I can call inside the loop and every time it counts the total rows of each table for me
declare
Cursor C_TABLE is
select trim(table_name) as table_name
from all_tables
where table_name in ('T1', 'T2', 'T3');
V_ROWNUM number;
begin
for m in C_TABLE
loop
for i in ( select column_name
from (
select c.column_name
from all_tab_columns c
where c.table_name = m.table_name
and c.owner = 'owner1'
)
)
loop
--I have this:
execute immediate ' insert into MY-table value (select ' || i.column_name || ' from ' || m.table_name || ')';
--I want this but it does not work of course:
V_ROWNUM := execute immediate 'select count(*) from ' || m.table_name;
execute immediate ' insert into MY-table value (select ' || i.column_name || ', ' || V_ROWNUM || ' from ' || m.table_name || ')';
end loop;
end loop;
end;
/
I count not use the "insert into" because I am not selecting from 1 table but the table I want to select from changes every round.
There are three things wrong with your dynamic SQL.
EXECUTE IMMEDIATE is not a function: the proper syntax is execute immediate '<<query>>' into <<variable>>.
An INSERT statement takes a VALUES clause or a SELECT but not both. SELECT would be very wrong in this case. Also note that it's VALUES not VALUE.
COLUMN_NAME is a string literal in the dynamic SQL so it needs to be in quotes. But because the SQL statement is itself a string, quotes in dynamic strings need to be escaped so it should be `'''||column_name||'''.
So the corrected version will look something like this
declare
Cursor C_TABLE is
select trim(table_name) as table_name
from all_tables
where table_name in ('T1', 'T2', 'T3');
V_ROWNUM number;
begin
for m in C_TABLE
loop
for i in ( select column_name
from (
select c.column_name
from all_tab_columns c
where c.table_name = m.table_name
and c.owner = 'owner1'
)
)
loop
execute immediate 'select count(*) from ' || m.table_name into V_ROWNUM;
execute immediate 'insert into MY_table values ( ''' || i.column_name || ''', ' || V_ROWNUM || ')';
end loop;
end loop;
end;
/
Dynamic SQL is hard because it turns compilation errors into runtime errors. It is good practice to write the statements first as static SQL. Once you have got the basic syntax right you can convert it into dynamic SQL.
you can't assign the result of execute immediate to a variable. it is not a function.
but you can do it by using the into_clause e.g.
execute immediate 'select count(*) from ' || m.table_name into V_ROWNUM ;

How to find all columns in a table which containing the chr(0) character in oracle [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';

plsql List all table.column containing null values

I'd like to find all column of a set of table with null values in them.
I can find the table and column names
SELECT TABLE_NAME, COLUMN_NAME
FROM user_tab_cols
where nullable='Y'
and table_name in ('myTalbe', 'myTable2');
And also check if the are nulls
select count(*) from myTable where myColumn is null;
but how can I put this toghether to have as result
table_name column_name
myTable myColumn
myTable myCol2
myTable2 col4
An approach could be with some dynamic SQL, but this would require some PL/SQL code; the following only uses SQL to get the result you need:
select *
from (
select table_name,
column_name,
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('select count(*) c from '||table_name || ' where ' || column_name || ' is null')),'/ROWSET/ROW/C')) as rowcount
from user_tab_columns
where nullable='Y'
and table_name in ('myTalbe', 'myTable2')
)
where rowcount > 0
This could be an approach with dynamic SQL:
declare
type tableOfNumber is table of number;
type tableOfChar is table of varchar2(30);
--
vSQl varchar2(4000);
vListNumbers tableOfNumber;
vListTables tableOfChar;
vListColumns tableOfChar;
begin
select listagg( 'select ''' ||
table_name || ''' as table_name, ''' ||
column_name || ''' as column_name, count(*) as rowCount from ' ||
table_name ||
' where ' ||
column_name ||
' is null having count(*) > 0' ,
' UNION ALL '
) within group ( order by table_name, column_name)
into vSQL
from user_tab_columns
where nullable='Y'
and table_name in ('myTalbe', 'myTable2');
--
dbms_output.put_line(vSQL);
/* whatever you may want to do with the query */
/* for example, fetch into some variables and print the result */
execute immediate vSQL
bulk collect into vListTables, vListColumns, vListNumbers;
--
if vListTables.count() > 0 then
for i in vListTables.first .. vListTables.last loop
dbms_output.put_line('Table ' || vListTables(i) ||
', column ' || vListColumns(i) ||
', number of nulls: ' || vListNumbers(i)
);
end loop;
end if;
end;
Here's a routine I wrote a while back to do exactly that. It will output the required DDL to make those columns that are nullable (but do not contain any nulls) not nullable.
https://connormcdonald.wordpress.com/2016/03/11/tightening-up-your-data-model/
It can do the task for a schema or a single table.

Oracle: how to run this query (generated column names)

I need to run a query on generated generated column names.
Here's the query:
select 'col_'||4 from MY_TABLE
Note:
"4" is a variable that is passed to this query from within the Java code
MY_TABLE is a table that contain columns with names (col_4, col_5, etc..)
Inside Oracle you need use dynamic SQL. (YourVariable value is 4 for your example)
EXECUTE IMMEDIATE ' select col_' || YourVariable || ' from MY_TABLE ';
From Java you can build any SQL and execute them
To run a dynamic SELECT statement, you have two choices:
For single row selects, you use EXECUTE IMMEDIATE ... INTO:
EXECUTE IMMEDIATE 'select col_' || l_num || ' from MY_TABLE WHERE id = 37' INTO l_result;
For selecting multiple rows, you can use a dynamic cursor:
DECLARE
TYPE MyCurType IS REF CURSOR;
my_cv MyCurType;
BEGIN
OPEN emp_cv FOR 'select col_' || l_num || ' from MY_TABLE';
...
END;
This code generates a SELECT that returns the tables with their column name:
SELECT
'SELECT ' ||(
SELECT
LISTAGG(
c.TABLE_NAME || '.' || c.COLUMN_NAME || ' AS "' || c.TABLE_NAME || '.' || c.COLUMN_NAME || '"',
', '
) WITHIN GROUP(
ORDER BY
c.TABLE_NAME
) "TABLE_NAMES"
FROM
USER_TAB_COLS c
WHERE
TABLE_NAME IN(
'PESSOA',
'PESSOA_FISICA',
'PESSOA_JURIDICA'
)
)|| 'FROM PERSON;'
FROM
DUAL;