Loop through all tables in database to fetch 2 rows per table - sql

I have a database wherein there are multiple owners and some tables are empty.
Currently I want to sequentially execute the same fetch query against all non-empty tables of a specific owner.
I wrote the PL/SQL below, but got this error "ORA-06550: line 8, column 41: PLS-00497: cannot mix between single row and multi-row (BULK) in INTO list 06550. 00000 - "line %s, column %s:\n%s".
DECLARE
CURSOR details IS
SELECT table_name
FROM all_tables
WHERE owner = 'emp' AND num_rows > 0;
myvar all_tables.table_name%TYPE;
rows_num NATURAL := 2;
sql_stmt VARCHAR2(1000);
BEGIN
OPEN details;
LOOP
FETCH details BULK COLLECT INTO myvar LIMIT rows_num;
END LOOP;
CLOSE details;
END;
How can I fix this?
edit: New code with SELECT included:
BEGIN
OPEN details;
LOOP
FETCH details INTO my_var;
EXIT WHEN myvar%NOTFOUND;
sql_stmt := 'SELECT * FROM ' || my_var|| ' WHERE ROWNUM <= :rows_num';
EXECUTE IMMEDIATE sql_stmt;
DBMS_OUTPUT.PUT_LINE(sql_stmt);
END LOOP;
CLOSE table_cur;
END;
No more errors now but I couldn't see anything in the Dbms output.

As P3CONSULTING said - declare TYPE of TABLE of CURSOR%ROWTYPE and then a variable that has type that newly declared TABLE OF type. Also please do not forget to add condition in loop to avoid infinite loop. Please try this code:
DECLARE
CURSOR details IS
SELECT table_name
FROM all_tables
WHERE owner = 'emp' AND num_rows > 0;
type t_var is table of details%rowtype;
myvar t_var;
rows NATURAL := 2;
BEGIN
OPEN details;
LOOP
FETCH details BULK COLLECT INTO myvar LIMIT rows;
EXIT WHEN myvar.count = 0;
END LOOP;
CLOSE details;
END;

The variable into which you "BULK COLLECT" must be a "TABLE OF..." not a single ROWTYPE.

If you do not need an output to be of specific format, then you may use dbms_xmlgen to execute dynamic SQL and serialize result into XML document.
Setup:
create table t0
as
select *
from user_tables
where 1=0
create table t1
as
select level as id
from dual
connect by level < 5
4 rows affected
create table t2
as
select *
from all_objects
where rownum = 1
1 rows affected
create table t3
as
select *
from all_source
where rownum < 5
4 rows affected
The query:
select * from (
select
owner, table_name
, dbms_xmlgen.getxml(
replace(replace (
'select * from "schema"."table" where rownum < 3'
, 'schema', owner)
, 'table', table_name
)
) as res
from all_tables
where owner = user
)
where res is not null
OWNER
TABLE_NAME
RES
FIDDLE_AIMBWFSLKTLDLGCECWIY
T1
<?xml version="1.0"?><ROWSET> <ROW>  <ID>1</ID> </ROW> <ROW>  <ID>2</ID> </ROW></ROWSET>
FIDDLE_AIMBWFSLKTLDLGCECWIY
T2
<?xml version="1.0"?><ROWSET> <ROW>  <OWNER>SYS</OWNER>  <OBJECT_NAME>ORA$BASE</OBJECT_NAME>  <OBJECT_ID>134</OBJECT_ID>  <OBJECT_TYPE>EDITION</OBJECT_TYPE>  <CREATED>17-AUG-21</CREATED>  <LAST_DDL_TIME>17-AUG-21</LAST_DDL_TIME>  <TIMESTAMP>2021-08-17:23:05:16</TIMESTAMP>  <STATUS>VALID</STATUS>  <TEMPORARY>N</TEMPORARY>  <GENERATED>N</GENERATED>  <SECONDARY>N</SECONDARY>  <NAMESPACE>64</NAMESPACE>  <SHARING>NONE</SHARING>  <ORACLE_MAINTAINED>Y</ORACLE_MAINTAINED>  <APPLICATION>N</APPLICATION>  <DUPLICATED>N</DUPLICATED>  <SHARDED>N</SHARDED>  <IMPORTED_OBJECT>N</IMPORTED_OBJECT> </ROW></ROWSET>
FIDDLE_AIMBWFSLKTLDLGCECWIY
T3
<?xml version="1.0"?><ROWSET> <ROW>  <OWNER>SYS</OWNER>  <NAME>STANDARD</NAME>  <TYPE>PACKAGE</TYPE>  <LINE>1</LINE>  <TEXT>package STANDARD AUTHID CURRENT_USER is -- careful on this line; SED edit occurs!</TEXT>  <ORIGIN_CON_ID>1</ORIGIN_CON_ID> </ROW> <ROW>  <OWNER>SYS</OWNER>  <NAME>STANDARD</NAME>  <TYPE>PACKAGE</TYPE>  <LINE>2</LINE>  <TEXT></TEXT>  <ORIGIN_CON_ID>1</ORIGIN_CON_ID> </ROW></ROWSET>
fiddle

Combining everything you need the below.Which satisfies all your requirements.The Sql fiddle here
DECLARE
CURSOR details IS
SELECT a.table_name, a.owner
FROM all_tables a
WHERE a.owner = user
AND a.num_rows > 0;
type t_var is table of details%rowtype;
myvar t_var;
rows1 NATURAL := 2;
v_plsql varchar2(32767);
i number := 0;
BEGIN
OPEN details;
LOOP
dbms_output.enable;
FETCH details BULK COLLECT
INTO myvar;
if NOT myvar.EXISTS(1) THEN
EXIT;
end if;
for i in myvar.first .. myvar.last loop
if NOT myvar.EXISTS(i) THEN
EXIT;
end if;
v_plsql := 'DECLARE ' || ' type rec_row is table of ' || myvar(i)
.table_name || '%rowtype; ' || 'l_row rec_row;' || 'BEGIN ' ||
' SELECT * ' || ' BULK COLLECT INTO l_row ' ||
' FROM ' || myvar(i).table_name || ' where rownum<=' ||
rows1 || ';' ||
' if l_row.EXISTS(1) then dbms_output.put_line(''' || i ||
' table and 1 row' || '''' || '); dbms_output.put_line(''';
for rec in (select a.*,
(select max(column_id)
from all_tab_cols a
where table_name = myvar(i).table_name) max_column
from all_tab_cols a
where table_name = myvar(i).table_name
order by column_id) loop
v_plsql := v_plsql || rec.column_name;
if rec.column_id <> rec.max_column then
v_plsql := v_plsql || ',';
end if;
end loop;
v_plsql := v_plsql || '''); dbms_output.put_line(';
for rec in (select a.*,
(select max(column_id)
from all_tab_cols a
where table_name = myvar(i).table_name) max_column
from all_tab_cols a
where table_name = myvar(i).table_name
order by column_id) loop
v_plsql := v_plsql || ' l_row(1).' || rec.column_name;
if rec.column_id <> rec.max_column then
v_plsql := v_plsql || '||'',''||';
end if;
end loop;
v_plsql := v_plsql || '); end if;';
v_plsql := v_plsql ||
'if l_row.EXISTS(2) then dbms_output.put_line(''' || i ||
' table and 2 row' || '''' || '); dbms_output.put_line(''';
for rec in (select a.*,
(select max(column_id)
from all_tab_cols a
where table_name = myvar(i).table_name) max_column
from all_tab_cols a
where table_name = myvar(i).table_name
order by column_id) loop
v_plsql := v_plsql || rec.column_name || '';
if rec.column_id <> rec.max_column then
v_plsql := v_plsql || ',';
end if;
end loop;
v_plsql := v_plsql || '''); dbms_output.put_line(';
for rec in (select a.*,
(select max(column_id)
from all_tab_cols a
where table_name = myvar(i).table_name) max_column
from all_tab_cols a
where table_name = myvar(i).table_name
order by column_id) loop
v_plsql := v_plsql || ' l_row(2).' || rec.column_name;
if rec.column_id <> rec.max_column then
v_plsql := v_plsql || '||'',''||';
end if;
end loop;
v_plsql := v_plsql || '); end if;';
v_plsql := v_plsql || ' end;';
--dbms_output.put_line(v_plsql);
execute immediate v_plsql;
end loop;
exit;
END LOOP;
CLOSE details;
END;
/

Related

PL/SQL Dynamic SQL : Table name not valid

I'm currently learning PL/SQL. I need to create a PL/SQL block to create a backup of all my tables like this : myTable -> myTable_old.
Here's what I got right now :
DECLARE
Cursor c IS SELECT table_name
FROM user_tables
WHERE table_name NOT LIKE '%_old';
sql_slc VARCHAR2(200);
sql_drp VARCHAR2(200);
sql_crt VARCHAR2(200);
row_count NUMBER;
t_name user_tables.table_name%type;
t_backup_name user_tables.table_name%type;
BEGIN
sql_drp := 'DROP TABLE :1 CASCADE';
sql_crt := 'CREATE TABLE :1 AS SELECT * FROM :2';
sql_slc := 'SELECT COUNT(*) FROM user_tables WHERE table_name = :1';
OPEN c;
LOOP
FETCH c INTO t_name;
EXIT WHEN (c%NOTFOUND);
t_backup_name := t_name || '_old';
dbms_output.put_line(t_name || ' ' || t_backup_name);
EXECUTE IMMEDIATE sql_slc INTO row_count USING t_backup_name;
IF row_count > 0 THEN
dbms_output.put_line(t_backup_name || ' dropped');
EXECUTE IMMEDIATE sql_drp USING t_backup_name;
END IF;
dbms_output.put_line(t_backup_name || ' created');
EXECUTE IMMEDIATE sql_crt USING t_backup_name, t_name;
COMMIT;
END LOOP;
CLOSE c;
END;
/
Here's the error :
OUVRAGE OUVRAGE_old
OUVRAGE_old created
DECLARE
*
ERROR on line 1 :
ORA-00903: table name not valid
ORA-06512: on line 29
I don't understand why this error is coming up, can someone help me ?
The issue is that you can not use bind variables for table names; Oracle documentation:
The database uses the values of bind variables exclusively and does
not interpret their contents in any way.
You should edit your code to use concatenation instead:
DECLARE
Cursor c IS SELECT table_name
FROM user_tables
WHERE table_name NOT LIKE '%_OLD'; /* OLD, upper case */
sql_slc VARCHAR2(200);
--sql_drp VARCHAR2(200);
--sql_crt VARCHAR2(200);
row_count NUMBER;
t_name user_tables.table_name%type;
t_backup_name user_tables.table_name%type;
BEGIN
-- sql_drp := 'DROP TABLE :1 CASCADE';
-- sql_crt := 'CREATE TABLE :1 AS SELECT * FROM :2';
sql_slc := 'SELECT COUNT(*) FROM user_tables WHERE table_name = :1';
OPEN c;
LOOP
FETCH c INTO t_name;
EXIT WHEN (c%NOTFOUND);
t_backup_name := t_name || '_OLD'; /* OLD, upper case */
DBMS_OUTPUT.put_line (t_name || ' ' || t_backup_name);
EXECUTE IMMEDIATE sql_slc INTO row_count USING t_backup_name;
IF row_count > 0
THEN
DBMS_OUTPUT.put_line (t_backup_name || ' dropped');
-- EXECUTE IMMEDIATE sql_drp USING t_backup_name;
EXECUTE IMMEDIATE ' drop table ' || t_backup_name; /* concatenation and not bind variables */
END IF;
DBMS_OUTPUT.put_line (t_backup_name || ' created'); /* concatenation and not bind variables */
-- EXECUTE IMMEDIATE sql_crt USING t_backup_name, t_name;
EXECUTE IMMEDIATE 'create table ' || t_backup_name || ' as select * from ' || t_name;
COMMIT;
END LOOP;
CLOSE c;
END;
Also, notice that, if not double quoted, object names always are uppercase, so you have to look for t_name || '_OLD' and not t_name || '_old'

How do i copy old columns to my new existing table?

I have an existing new table that has no columns yet. I want to copy all 10 columns in my old table. How do i do that? I don't want to drop the table so I could perform: create table newTable as select * from oldTable.
I don't want to drop the table so I could perform:
create table newTable as select * from oldTable.
Assuming your new table has at least 1 column BobC told you cannot have a table without any column, you can alter your table.
Alter new_table add (col1 varchar2(10)) , col2 ...);
Note that you need to mention all the column same as old table manually here.
If you don't want to do this manually then probably you would need a PLSQL block to do this.
CREATE OR REPLACE PROCEDURE creat_tbl_frm_tbl (tablname VARCHAR2)
AS
db_user VARCHAR2 (100) := USER;
TYPE t_rec IS RECORD
(
COLUMN_NAME VARCHAR2 (30 BYTE),
DATA_TYPE VARCHAR2 (106 BYTE),
DATA_LENGTH NUMBER
);
TYPE t_record IS TABLE OF t_rec;
-- initialization of record
v_record t_record := t_record ();
v_cntr NUMBER := 0;
v_status NUMBER;
sql_stmt VARCHAR2 (2000);
BEGIN
---dropping Already Existing temporary table
BEGIN
SELECT 1
INTO v_status
FROM all_objects
WHERE UPPER (object_name) = UPPER (tablname) AND owner = db_user;
--dbms_output.put_line( v_status );
IF v_status = 1
THEN
sql_stmt := 'drop table ' || tablname || '_new';
EXECUTE IMMEDIATE sql_stmt;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line ('Table not found--' || tablname);
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Error while dropping table-' || SQLCODE || SQLERRM);
END;
---------------------------------------------------------------------------
---- retrieving the columns of the table
BEGIN
SELECT column_name, data_type, data_length
BULK COLLECT INTO v_record
FROM all_tab_columns
WHERE table_name = UPPER (tablname)
ORDER BY column_id;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Error while retrieving the column details-' || SQLCODE || SQLERRM);
END;
FOR i IN 1 .. v_record.COUNT
LOOP
v_cntr := v_cntr + 1;
IF v_cntr = 1
THEN
IF v_record (i).data_type = 'DATE'
OR UPPER (v_record (i).data_type) = 'TIMESTAMP(6)'
THEN
sql_stmt :=
'create table '
|| ' '
|| tablname
|| '_new'
|| '('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| ')';
ELSE
sql_stmt :=
'create table '
|| ' '
|| tablname
|| '_new'
|| '('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| '('
|| v_record (i).data_length
|| '))';
END IF;
--dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt;
ELSE
IF v_record (i).data_type = 'DATE'
OR UPPER (v_record (i).data_type) = 'TIMESTAMP(6)'
THEN
sql_stmt :=
'alter table'
|| ' '
|| tablname
|| '_new'
|| ' '
|| 'add ('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| ')';
ELSE
sql_stmt :=
'alter table'
|| ' '
|| tablname
|| '_new'
|| ' '
|| 'add ('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| '('
|| v_record (i).data_length
|| '))';
END IF;
--dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt;
END IF;
END LOOP;
DBMS_OUTPUT.put_line ('Process Completed Successfully..!!');
END;
Output:
SQL> exec creat_tbl_frm_tbl('table2')
Process Completed Successfully..!!
PL/SQL procedure successfully completed.
SQL> select * from table2;
no rows selected
-- This is created with same columns as of passed table table2
SQL> select * from table2_new;
no rows selected
SELECT col1,col2,col3 --...
INTO newTable
FROM oldTable

Query for particular integer value from multiple columns with number datatype

I try to search a number from multiple columns (datatype number), but get ORA-01722: invalid number error.
My Query:
SELECT *
FROM CAMPAIGN
WHERE 1481125 IN (select column_name
from all_tab_columns
where table_name = 'CAMPAIGN'
AND data_type = 'NUMBER');
What is wrong with it?
You are comparing your number 1481125 with the names of the each column, not the values of each column in your table.
To go from a column's name (from dba_tab_columns) to the values in that column, you need to use some form of dynamic SQL. Here's a relatively simple example:
DECLARE
-- Since I don't have your CAMPAIGN table or data, I'm using DBA_OBJECTS in it's place.
l_table_name VARCHAR2 (30) := 'DBA_OBJECTS';
l_search_number NUMBER := 20; -- 1481125 in your example
l_record dba_objects%ROWTYPE;
l_sql VARCHAR2 (32000);
l_column_number NUMBER := 0;
l_cur SYS_REFCURSOR;
BEGIN
-- First: build dynamic SQL statement of the form:
-- SELECT * FROM table_name WHERE
-- ( ( col_name_a = 20 ) OR ( col_name_b = 20 ) OR ... )
l_sql := 'SELECT * FROM dba_objects WHERE ( ';
FOR r_number_column IN (SELECT column_name
FROM dba_tab_columns
WHERE table_name = l_table_name
AND data_type = 'NUMBER'
ORDER BY column_id) LOOP
IF l_column_number > 0 THEN
l_sql := l_sql || ' OR ';
END IF;
l_column_number := l_column_number + 1;
l_sql := l_sql || '(' || r_number_column.column_name || ' = ' || l_search_number || ')';
END LOOP;
IF l_column_number = 0 THEN
-- No number columns in table, so there should be no matches
l_sql := l_sql || ' 1=0';
END IF;
l_sql := l_sql || ')';
DBMS_OUTPUT.put_line (l_sql);
OPEN l_cur FOR l_sql;
LOOP
FETCH l_cur INTO l_record;
EXIT WHEN l_cur%NOTFOUND;
DBMS_OUTPUT.put_line ('Object Name ' || l_record.object_name || ' has search number ' || l_search_number);
END LOOP;
END;
Your query is:
SELECT * FROM CAMPAIGN WHERE 1481125 IN
(select column_name from all_tab_columns where table_name = 'CAMPAIGN' AND data_type='NUMBER')
Breaking that down we have:
SELECT * FROM CAMPAIGN WHERE 1481125 IN (<a set of numbers>)
and the subquery:
select column_name from all_tab_columns
where table_name = 'CAMPAIGN'
AND data_type='NUMBER'
That subquery is going to return a list of column names e.g.
CAMPAIGN_COUNT
CAMPAIGN_ID
CAMPAIGN_NUMBER_OF_SOMETHINGS
Your query is thus equivalent to:
SELECT * FROM CAMPAIGN WHERE 1481125 IN
('CAMPAIGN_COUNT', 'CAMPAIGN_ID', 'CAMPAIGN_NUMBER_OF_SOMETHINGS')
You can see why you would get the ORA-01722 error there?
You would need to write dynamic SQL to achieve your aim.

Oracle procedure cursor loop errors

Can you tell me what is wrong with this procedure that I'm trying to create?
CREATE OR REPLACE PROCEDURE create_audit_tables (table_owner VARCHAR2)
IS
CURSOR c_tables (
table_owner VARCHAR2)
IS
SELECT ot.owner AS owner, ot.table_name AS table_name
FROM all_tables ot
WHERE ot.owner = table_owner
AND ot.table_name NOT LIKE 'AUDIT_%'
AND ot.table_name <> 'EXAUDIT'
AND NOT EXISTS
(SELECT 1
FROM EXAUDIT efa
WHERE ot.table_name = efa.tname)
AND NOT EXISTS
(SELECT 1
FROM all_tables at
WHERE at.table_name = 'AUDIT_'||ot.table_name);
v_sql VARCHAR2 (8000);
v_count NUMBER := 0;
v_aud VARCHAR2 (30);
BEGIN
FOR r_table IN c_tables (table_owner)
LOOP
BEGIN
v_aud := 'AUDIT_'||r_table.table_name;
v_sql :=
'create table '
|| v_aud
|| ' as select * from '
|| r_table.owner
|| '.'
|| r_table.table_name
|| ' where 1 = 1';
DBMS_OUTPUT.put_line ('Info: ' || v_sql);
EXECUTE IMMEDIATE v_sql;
v_sql :=
'alter table '
|| v_aud
|| ' add ( AUDIT_ACTION char(1), AUDIT_BY varchar2(50), AUDIT_AT TIMESTAMP)';
EXECUTE IMMEDIATE v_sql;
v_count := c_tables%ROWCOUNT;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Failed to create table '
|| v_aud
|| ' due to '
|| SQLERRM);
END;
END LOOP;
IF v_count = 0
THEN
DBMS_OUTPUT.put_line ('No audit tables created');
ELSE
DBMS_OUTPUT.put_line (v_count || ' audit tables created.');
END IF;
END;
/
I checked it with show errors function and it gave me this:
LINE/COL ERROR
-------- --------------------------------------------
6/1 PL/SQL: SQL Statement ignored
16/3 PL/SQL: ORA-00906: missing left parenthesis
Perhaps the problem is quite simple, but I can't see it, so please help me if you can
I can compile procedure without problems after creating table
create table EXAUDIT (tname varchar2(100));
Check status of procedure:
select status from user_objects where lower(object_name) = 'create_audit_tables';

query all tables in Oracle

I have this plsql code that will give me all tables in the database with name CUSTOMERS. Now I am struck how to insert another loop in to this. I want to get the output from this code and pass it on to next loop where I want to query something like, select count(*) from Schema.customers; for each schema.
DECLARE
--c_id customers.id%type;
c_name all_tables.table_name%type;
c_tabs all_tables.owner%type;
CURSOR c_tables is
SELECT table_name, owner FROM all_tables where table_name='CUSTOMERS';
BEGIN
OPEN c_tables;
LOOP
FETCH c_tables into c_name, c_tabs;
dbms_output.put_line(c_tabs || '.' || c_name );
EXIT WHEN c_tables%notfound;
END LOOP;
CLOSE c_tables;
END;
/
------- Sample output of my code: ------------
UMICH2.CUSTOMERS
TRINITYDC.CUSTOMERS
BUFFALO.CUSTOMERS
SNOW.CUSTOMERS
PULASKITECH.CUSTOMERS
RARITANVAL.CUSTOMERS
STMARYSCA.CUSTOMERS
You can get the same result in a single SQL statement
SELECT table_name
,to_number
(extractvalue
(xmltype
(dbms_xmlgen.getxml
('SELECT count(*) c FROM ' || owner || '.' || table_name)
)
,'/ROWSET/ROW/C'
)
) Count
FROM all_tables
WHERE table_name = 'CUSTOMERS'
This way is one possibility that you can do that.
DECLARE
--c_id customers.id%type;
c_name all_tables.table_name%TYPE;
c_tabs all_tables.owner%TYPE;
v_value PLS_INTEGER;
CURSOR c_tables IS
SELECT table_name
FROM all_tables
WHERE table_name = 'CUSTOMERS';
CURSOR c_owner IS
SELECT DISTINCT owner
FROM all_tables
WHERE table_name = 'CUSTOMERS';
BEGIN
OPEN c_tables;
LOOP
FETCH c_tables INTO c_name;
OPEN c_owner;
LOOP
FETCH c_owner INTO c_tabs;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(1) FROM ' || c_tabs || '.' || c_name INTO v_value;
EXCEPTION
WHEN other THEN
NULL;
END;
DBMS_OUTPUT.put_line ( v_value );
EXIT WHEN c_owner%NOTFOUND;
END LOOP;
CLOSE c_owner;
DBMS_OUTPUT.put_line ( c_tabs || '.' || c_name );
EXIT WHEN c_tables%NOTFOUND;
END LOOP;
CLOSE c_tables;
END;
Any question just let me know.
Thanks.