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.
Related
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
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;
I'm trying to code a statement that checks if a table exist and if it does to truncate/delete it. If it doesn't exist, to print the message 'This table does not exist!'
This is what I've come up so far but doesn't seem to work.
BEGIN
TRUNCATE TABLE PPA_P6_2018;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -942 THEN
DBMS_OUTPUT.put_line('This table does not exist!');
ELSE
RAISE;
DBMS_OUTPUT.put_line('This table has been delted!');
END IF;
END;
Create a custom exception to catch when the table does not exist and then only catch that single exception (rather than catching all of them with OTHERS) and then use EXECUTE IMMEDIATE to truncate/drop the table:
DECLARE
table_name VARCHAR2(30) := 'PPA_P6_2018';
table_not_exists EXCEPTION;
PRAGMA EXCEPTION_INIT( table_not_exists, -942 );
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || table_name;
EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;
DBMS_OUTPUT.put_line('This table has been deleted!');
EXCEPTION
WHEN table_not_exists THEN
DBMS_OUTPUT.put_line('This table does not exist!');
END;
/
db<>fiddle
TRUNCATE TABLE is DDL so cannot be run directly within PL/SQL. You need to use EXECUTE IMMEDIATE:
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE PPA_P6_2018';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -942 THEN
DBMS_OUTPUT.put_line('This table does not exist!');
ELSE
RAISE;
DBMS_OUTPUT.put_line('This table has been delted!');
END IF;
END;
Note: You will never see the message 'This table has been delted!' since the RAISE before it throws you out of the block! And if you got an error, the table hasn't been deleted anyway!
DECLARE
v_count NUMBER;
v_table_name VARCHAR2 (100) := 'YOURTABLE';
v_sqlstr VARCHAR2 (1000);
BEGIN
SELECT COUNT ( * )
INTO v_count
FROM user_tables
WHERE table_name = v_table_name;
IF v_count > 0
THEN
v_sqlstr := 'DELETE FROM ' || v_table_name;
EXECUTE IMMEDIATE v_sqlstr;
DBMS_OUTPUT.put_line (
'TABLE ' || v_table_name || ' HAS BEEN DELETED!');
ELSE
DBMS_OUTPUT.put_line ('TABLE ' || v_table_name || ' DOES NOT EXIST!');
END IF;
END;
I am currently looping through values in PL/SQL with the following:
for c in (select * from example_table where name is not null) loop
-- logic
end loop;
I would like to replace the SQL statement with a dynamic one, for example:
l_sql := 'select * from example_table where || l_col || is not null';
for c in (l_sql) loop
-- logic
end loop;
Is this possible?
Best regards
That's not possible with an implicit cursor loop ( select inside for loop ). You may use the conventional OPEN .. FETCH .. LOOP through a REFCURSOR with a record variable of tablename%ROWTYPE
DECLARE
t_rec example_table%ROWTYPE;
l_sql VARCHAR2(1000);
v_cur SYS_REFCURSOR;
l_col varchar2(32) := 'MY_COLUMN';
BEGIN
l_sql := 'select * from example_table where '|| l_col || ' is not null';
OPEN v_cur FOR l_sql;
LOOP
FETCH v_cur INTO t_rec; --fetch a row
EXIT WHEN v_cur%NOTFOUND;
-- your logic using t_rec columns.
END LOOP;
CLOSE v_cur;
END;
/
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 ;