Skip to Main Content
  • Questions
  • Loop through multiple tables and print out a limited number of rows per table

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Anthony.

Asked: January 09, 2023 - 1:48 pm UTC

Last updated: January 11, 2023 - 1:59 pm UTC

Version: Oracle Database 19c

Viewed 1000+ times

You Asked

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?

and Chris said...

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"}


Is this answer out of date? If it is, please let us know via a Comment

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library