I have a database wherein there are multiple owners and some tables are empty.
Currently I want to sequentially execute the same query against all non-empty tables of a specific owner.
I wrote the PL/SQL below:
DECLARE
CURSOR table_cur IS
SELECT table_name FROM all_tables
WHERE owner = 'EMP' AND ROWNUM > 0;
table_name all_tables.table_name%TYPE;
rows_num NATURAL := 2;
sql_stmt VARCHAR2(1000);
outp VARCHAR2(1000);
BEGIN
OPEN table_cur;
LOOP
FETCH table_cur INTO table_name;
EXIT WHEN table_cur%NOTFOUND;
sql_stmt := 'SELECT * FROM ' || table_name || ' WHERE ROWNUM <= :rows_num';
DBMS_OUTPUT.PUT_LINE(sql_stmt);
EXECUTE IMMEDIATE sql_stmt INTO outp USING rows_num;
DBMS_OUTPUT.PUT_LINE(outp);
END LOOP;
CLOSE table_cur;
END;
I'm aware that varchar2(1000) isn't the right data type for outp so when I ran this, nothing came up DBMS_OUTPUT. Is there anything I could do to fix this?
There are a couple of key issues:
- The query will return :rows_num per table; the EXECUTE IMMEDIATE expects at most one row.
- SELECT * returns all the columns in each table
You can get around the first issue by BULK COLLECTing into an array. Then loop through the array to display the results.
The second problem is harder - you need to fetch into variables matching the columns for each table. Which vary for each row.
As you're on 19c there is a workaround. Use JSON_OBJECT(*) to convert each row into a JSON object and fetch that into an array (of CLOBs).
A few other notes:
- AND ROWNUM > 0 - this has no effect!
- When using string concatenation to construct SQL statements for dynamic SQL you need to be wary of SQL injection
- 12c added the FETCH FIRST :n ROWS ONLY to get N rows from a table
declare
cursor table_cur is
select table_name
from all_tables
where owner = 'HR';
table_name all_tables.table_name%type;
rows_num natural := 2;
sql_stmt clob;
type json_nt is table of clob;
json_rows json_nt;
begin
open table_cur;
loop
fetch table_cur into table_name;
exit when table_cur%notfound;
sql_stmt :=
'select json_object(*) from hr.' || table_name || ' where rownum <= :rows_num';
execute immediate sql_stmt
bulk collect
into json_rows
using rows_num;
dbms_output.put_line ( table_name );
for rec in 1 .. json_rows.count loop
dbms_output.put_line( json_rows(rec) );
end loop;
end loop;
close table_cur;
end;
/
COUNTRIES
{"COUNTRY_ID":"IT","COUNTRY_NAME":"Italy","REGION_ID":1}
{"COUNTRY_ID":"JP","COUNTRY_NAME":"Japan","REGION_ID":3}
DEPARTMENTS
{"DEPARTMENT_ID":10,"DEPARTMENT_NAME":"Administration","MANAGER_ID":200,"LOCATION_ID":1700}
{"DEPARTMENT_ID":20,"DEPARTMENT_NAME":"Marketing","MANAGER_ID":201,"LOCATION_ID":1800}
EMPLOYEES
{"EMPLOYEE_ID":100,"FIRST_NAME":"Steven","LAST_NAME":"King","EMAIL":"SKING","PHONE_NUMBER":"515.123.4567","HIRE_DATE":"2003-06-17T00:00:00","JOB_ID":"AD_PRES","SALARY":24000,"COMMISSION_PCT":null,"MANAGER_ID":null,"DEPARTMENT_ID":90}
{"EMPLOYEE_ID":101,"FIRST_NAME":"Neena","LAST_NAME":"Kochhar","EMAIL":"NKOCHHAR","PHONE_NUMBER":"515.123.4568","HIRE_DATE":"2005-09-21T00:00:00","JOB_ID":"AD_VP","SALARY":17000,"COMMISSION_PCT":null,"MANAGER_ID":100,"DEPARTMENT_ID":90}
JOBS
{"JOB_ID":"AD_PRES","JOB_TITLE":"President","MIN_SALARY":20080,"MAX_SALARY":40000}
{"JOB_ID":"AD_VP","JOB_TITLE":"Administration Vice President","MIN_SALARY":15000,"MAX_SALARY":30000}
JOB_HISTORY
{"EMPLOYEE_ID":102,"START_DATE":"2001-01-13T00:00:00","END_DATE":"2006-07-24T00:00:00","JOB_ID":"IT_PROG","DEPARTMENT_ID":60}
{"EMPLOYEE_ID":101,"START_DATE":"1997-09-21T00:00:00","END_DATE":"2001-10-27T00:00:00","JOB_ID":"AC_ACCOUNT","DEPARTMENT_ID":110}
LOCATIONS
{"LOCATION_ID":1000,"STREET_ADDRESS":"1297 Via Cola di Rie","POSTAL_CODE":"00989","CITY":"Roma","STATE_PROVINCE":null,"COUNTRY_ID":"IT"}
{"LOCATION_ID":1100,"STREET_ADDRESS":"93091 Calle della Testa","POSTAL_CODE":"10934","CITY":"Venice","STATE_PROVINCE":null,"COUNTRY_ID":"IT"}
REGIONS
{"REGION_ID":1,"REGION_NAME":"Europe"}
{"REGION_ID":2,"REGION_NAME":"Americas"}