I want to find the columns in all the tables that could have the specific data or value in Oracle.
Example:tom#gmail.com
How can I narrow down on the tables that might have the email value?
--TABLE OR VIEW DOES NOT EXIST
SET SERVEROUTPUT ON SIZE 100000
DECLARE
match_count INTEGER;
-- Type the owner of the tables you are looking at
v_owner VARCHAR2(255) :='GWEB';
-- 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) :='tom#gmail.com';
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;
You:
Do not consider the owner of the table in the dynamic query.
May need to quote the identifiers.
Can also filter on the data_length.
Like this:
DECLARE
match_count INTEGER;
v_owner VARCHAR2(255) := 'GWEB';
v_data_type VARCHAR2(255) := 'VARCHAR2';
v_search_string VARCHAR2(4000) :='tom#example.com';
BEGIN
FOR t IN (
SELECT table_name,
column_name
FROM all_tab_cols
WHERE owner = v_owner
AND data_type = v_data_type
AND data_length >= LENGTH(v_search_string)
)
LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*)'
|| ' FROM "'||v_owner||'"."'||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;
/
db<>fiddle here
Related
I have an oracle database and I am running the below query
select table_name
from db_test.test
where table_name like '%20170128'
This returns me a column with all the tables with the specific date at the end.
How can I take this list and query them?
You'd need dynamic SQL. If you're running a simple query against each table (I'm just doing a count(*) in this example), something like this would work
declare
l_cnt integer;
l_sql varchar2(1000);
begin
for t in (select table_name
from db_test.test
where table_name like '%20170128')
loop
l_sql := 'select count(*) from ' || t.table_name;
execute immediate l_sql
into l_cnt;
dbms_output.put_line( t.table_name || ' has ' || l_cnt || ' rows.' );
end loop;
end;
When I execute the following code I get , sql command not ended properly.
I'm using this to find matching column for a string.
DECLARE
match_count INTEGER;
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 'XRWJ01';
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
/
Debug your code... if you get an error print the statement erroring out. I made some minor changes to your pl/sql block. Replace v_owner and v_datatype with your values to run it.
set serveroutput on size 999999
clear screen
DECLARE
match_count INTEGER;
v_owner VARCHAR2(30);
v_data_type VARCHAR2(30);
v_stmt VARCHAR2(1024);
BEGIN
v_owner := 'SCOTT';
v_data_type := 'NUMBER';
FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and data_type = v_data_type) LOOP
v_stmt := 'SELECT COUNT(*) FROM "'||t.table_name||'" WHERE "'||t.column_name||'" like :1';
BEGIN
EXECUTE IMMEDIATE
v_stmt
INTO match_count
USING 'XRWJ01';
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line( 'Error in statement: '|| v_stmt );
dbms_output.put_line ( DBMS_UTILITY.FORMAT_ERROR_STACK() );
dbms_output.put_line ( DBMS_UTILITY.FORMAT_ERROR_BACKTRACE() );
END;
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
/
I ran the statement locally and it errored out because of LOWER('||t.column_name||'). You select the columns from a data dictionary - those are the column names. If you do a LOWER then you might have different values if the tables/columns are created with case. Solution is to not do LOWER and put double quotes around them.
I have a PL/SQL in which I want to perform a search.
The PL/SQL is
DECLARE
match_count INTEGER;
BEGIN
FOR t IN (SELECT owner, table_name, column_name
FROM all_tab_columns
WHERE table_name='GETS_TS_EGU_LOOKUP' 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 'NONE';
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
/
This PL/SQL can perform look up on the keyword NONE. I want to perform a LIKE search on NONE like %NONE%.
How can I do it?
Is it even possible?
That should be
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM ' || t.owner || '.' || t.table_name ||
' WHERE ' || t.column_name || ' like ''%'' || :1 || ''%'''
INTO match_count
USING 'NONE';
But with 'NONE' being a constant, why not simply:
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM ' || t.owner || '.' || t.table_name ||
' WHERE ' || t.column_name || ' like ''%NONE%'''
INTO match_count;
You can declare a new variable to assign the string you want to search and then you cane use it in the query as shown below
DECLARE
match_count INTEGER;
srch_str varchar2(20);
BEGIN
srch_str:= 'NONE'
FOR t IN (SELECT owner, table_name, column_name
FROM all_tab_columns
WHERE table_name='GETS_TS_EGU_LOOKUP' and data_type LIKE '%CHAR%') LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM ' || t.owner || '.' || t.table_name ||
' WHERE '||t.column_name||' like ''%'||srch_str||'%'''
INTO match_count;
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
It will work perfectly fine.
This was what i was trying
SET SERVEROUTPUT ON;
DECLARE
sql_query VARCHAR2(32767);
BEGIN
FOR t IN (SELECT table_name, column_name FROM user_tab_columns)
LOOP
EXECUTE IMMEDIATE sql_query := 'SELECT * FROM ' || t.table_name ;
END LOOP;
END;
This query gives you the number of distinct values per column (assuming that statistics are up to date).
select owner, table_name, column_name, num_distinct
from all_tab_col_statistics
Maybe this would be enough.
If you need to have the distinct values, you have to modify the sql_query param in your script as follows:
EXECUTE IMMEDIATE sql_query := 'SELECT distinct '|| t.column_name ||
' FROM ' || t.table_name ;
If I have a table:
create table thisTable (
column1 varchar(20),
column2 varchar(20),
column3 varchar(20)
);
and I want to transfer data to a 1 column XML table:
create table XMLTable (
data1 sys.xmltype
);
<column2>
<column1>..</column1>
<column2>..</column2>
<column3>..</column3>
</column2>
How would I do that?
INSERT INTO XMLTABLE
SELECT
XMLELEMENT(
"column2",
XMLELEMENT("column1", COLUMN1), XMLELEMENT("column2", COLUMN2), XMLELEMENT("column3", COLUMN3)
)
FROM
thisTable;
You can insert it using below procedure.
declare
sourceTable varchar2(80) := 'THISTABLE';
destTable varchar2(80) := 'XMLTABLE';
destColumn varchar2(80) := 'data1';
TYPE cur_typ IS REF CURSOR;
c cur_typ;
colu varchar2(2000);
vsql varchar2(2000) := ' select ';
begin
for r in (select column_name from user_tab_columns where table_name = sourceTable order by column_id)
loop
vsql := vsql || ''' <' || r.column_name || '>'' || ' || r.column_name || ' || ''</' || r.column_name || '> '' || ' ;
end loop;
vsql := substr(vsql, 0 ,length(vsql)-4);
vsql := vsql ||' as x From ' || sourceTable;
open c for vsql ;
loop
FETCH c INTo colu;
EXIT WHEN c%NOTFOUND;
dbms_output.put_line(colu);
execute immediate ' insert into ' || destTable || ' values (xmltype(''<column2>' || colu || '</column2>'')) ';
end loop;
close c;
end;