Go over all columns in tables in Oracle - oracle

This is a noobie question, most likely syntax one. But I am kinda lost...
I need to go over all columns in all tables in Oracle to generate trigger script. That trigger should insert the row being updated to a log table which is nearly the same as the original table. I thought I would just go over all the columns and just concatenate strings. Fairly easy but I am struggling with the syntax...
Here's what I have so far:
DECLARE
cursor tableNames is
select table_name
from user_tables
where table_name not like '%_A';
lSql varchar2(3000);
type t_columnRow is ref cursor;
v_columns t_columnRow;
begin
FOR tableName in tableNames
LOOP
open v_columns for select COLUMN_NAME from user_tab_columns where table_name = tableName;
for columnRow in v_columns LOOP
DBMS_OUTPUT.PUT_LINE(tableName || '.' || columnRow.COLUMN_NAME);
-- Here I would just concatenate the strings ....
END LOOP;
END LOOP;
End;
For that I am getting the following error:
Error at line 1
ORA-06550: line 14, column 84:
PLS-00382: expression is of wrong type
ORA-06550: line 16, column 22:
PLS-00221: 'V_COLUMNS' is not a procedure or is undefined
ORA-06550: line 16, column 5:
PL/SQL: Statement ignored

Try this:
BEGIN
FOR t IN (SELECT table_name FROM user_tables WHERE table_name not like '%_A')
LOOP
FOR c IN (SELECT column_name FROM user_tab_columns WHERE table_name = t.table_name)
LOOP
DBMS_OUTPUT.PUT_LINE(t.table_name||'.'||c.column_name);
-- Here I would just concatenate the strings ....
END LOOP;
END LOOP;
END;

You may be able to get away with something as simple as this:
DECLARE
cursor tableNames is
select table_name
from user_tables
where table_name not like '%_A';
lSql varchar2(3000);
begin
FOR tableName in tableNames
LOOP
for columnRow in (select COLUMN_NAME from user_tab_columns where table_name = tableName) LOOP
DBMS_OUTPUT.PUT_LINE(tableName || '.' || columnRow.COLUMN_NAME);
-- Here I would just concatenate the strings ....
END LOOP;
END LOOP;
End;

Related

Table or view does not exist for a nested implicit cursor

DECLARE
sql_stmt varchar2(400);
cursor c1 is SELECT view_name from all_views where owner = 'owner1' AND view_name like 'IRV_%' OR view_name like 'RD_%' order by view_name;
BEGIN
for i IN c1 loop
sql_stmt := 'create table new_table as select * FROM owner1.view1 minus select * FROM owner2.view1';
dbms_output.put_line(sql_stmt);
execute immediate sql_stmt;
for ii IN (SELECT * from new_table) loop
dbms_output.put_line('inner loop');
end loop; -- for ii
execute immediate 'drop table new_table';
exit when c1%NOTFOUND;
end loop; -- for i
END;
I get in the script output:
ORA-06550: line 9, column 32:
PL/SQL: ORA-00942: table or view does not exist
Line 9 is: for ii IN (SELECT * from new_table) loop
Thank you in advance.
Ok HERE IS MY NEW CODE: Thank you GurV for your help.
DECLARE
CURSOR c1
IS
SELECT view_name
FROM all_views
WHERE owner = 'DBA_A'
AND view_name LIKE 'IRV_%'
OR view_name LIKE 'RD_%'
ORDER BY view_name;
BEGIN
FOR i IN c1
LOOP
FOR ii IN ('select * FROM DBA_A.' || i.VIEW_NAME || ' minus select * FROM DBA_B.' || i.VIEW_NAME)
LOOP
dbms_output.put_line('inner loop');
-- put the results from the select in the FOR ii loop in a listAgg string
-- do stuff
END LOOP; -- for ii
END LOOP; -- for i
END;
Error generated at END LOOP; -- for ii
PLS-00103: Encountered the symbol "END" when expecting one of the following:
The dbms_output.put_line shows the proper select is generated.
Thanks again.
Rich
Your code is not aware that you'll be creating a table dynamically (how could it?).
Generally, The PL/SQL Compiler will check your block for the following before executing it:
Check syntax.
Check semantics and security, and resolve names.
Generate (and optimize) bytecode (a.k.a. MCode).
It's on the 2nd Step where your code fails because There isn't a table named new_table prior to compile time.
I think there is no need of Dynamic SQL here. Also, you do not need to put an exit condition when using a for loop on cursor. You can do this:
DECLARE
CURSOR c1
IS
SELECT view_name
FROM all_views
WHERE owner = 'owner1'
AND view_name LIKE 'IRV_%'
OR view_name LIKE 'RD_%'
ORDER BY view_name;
BEGIN
FOR i IN c1
LOOP
FOR ii IN (select * FROM owner1.view1 minus select * FROM owner2.view1)
LOOP
DBMS_OUTPUT.put_line('Hey there');
-- do more stuff
END LOOP; -- for ii
END LOOP; -- for i
END;

dbms_out returning column 1: PLS-00428: an INTO clause is expected in SELECT statement

Running this query in sql-developer :
SET SERVEROUTPUT ON
BEGIN
DBMS_OUTPUT.PUT_LINE ('schema > table > key ');
SELECT 'table_name' TABLE_NAME , t.* FROM table_name t ;
END ;
I get this message :
Error report -
ORA-06550: line 4, column 1: PLS-00428: an INTO clause is expected in
this SELECT statement
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
while when I run the two statements one by one it works fine.
so why do I get the error and how to avoid it ?
many thanks,
You're confusing SQL and PL/SQL statements, as explained in comments. You can select into something, but you'd either get a single row of data, or select into a collection and then iterate over that manually, as Aramillo showed.
You can use a 'cursor for-loop' to iterate over your rows instead:
SET SERVEROUTPUT ON
BEGIN
DBMS_OUTPUT.PUT_LINE ('schema > table > key ');
FOR rec IN (SELECT 'table_name' TABLE_NAME , t.* FROM table_name t)
LOOP
DBMS_OUTPUT.PUT_LINE (rec.COLUMN_NAME1||','||rec.COLUMN_NAME2);
END LOOP;
END;
/
But given your starting point it's simpler to leave the query as plan SQL and run it after the block:
SET SERVEROUTPUT ON
BEGIN
DBMS_OUTPUT.PUT_LINE ('schema > table > key ');
END;
/
SELECT 'table_name' TABLE_NAME , t.* FROM table_name t;
or in SQL*Plus or SQL Developer, use the client prompt command instead of dbms_output:
prompt schema > table > key
SELECT 'table_name' TABLE_NAME , t.* FROM table_name t;
For example, if you want to print columns COLUMN_NAME of your table, you can do somthing like this:
DECLARE
TYPE TABLE_OBJ IS TABLE OF TABLE_NAME%ROWTYPE;
TABLE_T TABLE_OBJ;
BEGIN
SELECT * BULK COLLECT INTO TABLE_T FROM TABLE_NAME;
FOR i IN 1..TABLE_T.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(TABLE_T(i).COLUMN_NAME1||','||TABLE_T(i).COLUMN_NAME2);
END LOOP;
END;

Get the count of each table

I was trying to create a procedure to fetch the total count of rows of all tables corresponding to a schema.
I am proceeding with a cursor which store the total list of tables and and the same is iterate further. Even though the functionality is not tested.The procedure creation compiled with the following errors.
create or replace
PROCEDURE PROC_TABLE_COUNT
AS
table_count NUMBER;
CURSOR total_tables
IS
SELECT table_name FROM dba_tables WHERE owner = 'OWNER_NAME';
BEGIN
FOR i IN total_tables
LOOP
SELECT COUNT (*) INTO table_count FROM dba_tables db where db.table_name = i.table_name;
END LOOP;
END PROC_TABLE_COUNT;
1)Error(7,6): PL/SQL: SQL Statement ignored
2)Error(7,33): PL/SQL: ORA-00942: table or view does not exist
3)Error(11,1): PL/SQL: SQL Statement ignored
4)Error(11,76): PL/SQL: ORA-00904: "I"."TABLE_NAME": invalid identifier
5)Error(11,76): PLS-00364: loop index variable 'I' use is invalid
Question 1:
Is the error 2(at dba_tables) is due to the grant being denied? By right clicking on the procedure name ,I tried to assign the privilege to debug and execute. But still the error persists.
Question 2:
Regarding the invalid identifier. Why is this error coming?
UPDATE:
As per one of the valuable comment I have changed the query and the compile error is gone. Now there is an issue in the logic.
create or replace procedure proc_tab_count as
table_count NUMBER;
CURSOR total_tables
IS
SELECT table_name FROM user_tables;
BEGIN
FOR i IN total_tables
LOOP
SELECT COUNT (*) INTO table_count FROM user_tables WHERE db.table_name = i.table_name; --Wrong logic here
DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
END LOOP;
end proc_tab_count;
output is coming like:
Table1 -COUNT:1
Table2 -COUNT:1
Table3 -COUNT:1
Table3 -COUNT:1
Table4 -COUNT:1
Table5 -COUNT:1
Guess, you want to count rows in all your tables, and we need a dynamic SQL here.
EXCEUTE IMMEDIATE is used to frame the dynamic SQL making the OWNER.TABLE_NAME dynamic.
create or replace procedure proc_tab_count as
table_count NUMBER;
CURSOR total_tables
IS
SELECT table_name FROM user_tables;
BEGIN
FOR i IN total_tables
LOOP
/* Handle Exceptions */
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT (*) FROM '|| i.table_name INTO table_count;
DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
EXCEPTION WHEN OTHERS
THEN
DBMS_OUTPUT.put_line('Error while Querying '||i.table_name||'-Error:'||SQLERRM);
END;
END LOOP;
end proc_tab_count;
I'm not so sure that the accepted answer works. Does user_tables contain owner? Otherwise, it's spot on.
Anyway. Another approach using SYS_REFCURSOR:
CREATE OR REPLACE PROCEDURE proc_table_count
CURSOR total_tables IS
SELECT table_name
FROM user_tables
ORDER BY table_name;
table_count NUMBER;
v_sql VARCHAR2(100);
v_cursor SYS_REFCURSOR;
BEGIN
FOR i IN total_tables
LOOP
BEGIN
v_sql := 'SELECT COUNT (*) FROM '||i.table_name;
OPEN v_cursor FOR v_sql;
FETCH v_cursor INTO table_count;
CLOSE v_cursor;
DBMS_OUTPUT.put_line(i.table_name||'-COUNT:'||table_count);
EXCEPTION WHEN OTHERS
THEN
DBMS_OUTPUT.put_line('Error while Querying '||i.table_name||'-Error:'||SQLERRM);
END;
END LOOP;
end proc_tab_count;

updating values in a loop by column name

I have a table with several columns, and I would like to eliminate spaces value (' ') from the values. The query is:
update table
set column_name = trim(both ' ' from column_name)
where column_name like '% '
and since the table contains around 55 columns so probably a loop would be a feasible idea instead of writing update statement for each and every column.
First I checked if looping is working:
declare
column_name varchar2(2048);
cursor pointer is
select column_name into column_name from user_tab_cols where table_name = 'TABLE_NAME';
begin
for x in pointer
loop
dbms_output.put_line(x.column_name);
end loop;
end;
yes, it is working. I get column names in dbms_output window.
Now,
Here is what I am trying to do which doesn't seem to work:
declare
column_var varchar2(2048);
cursor pointer is
select column_name into column_var from user_tab_cols where table_name = 'TABLE_NAME';
begin
for x in pointer
loop
update table_name
set x.column_var = trim(both ' ' from x.column_var)
where x.column_var like '% ';
commit;
end loop;
end;
This is, unfortunately not working. This is error I get:
ORA-06550: line 11, column 18:
PLS-00302: component 'COLUMN_VAR' must be declared.
ORA-06550: line 11, column 16:
PL/SQL: ORA-00904: "X"."COLUMN_VAR": invalid identifier
ORA-06550: line 9, column 10:
PL/SQL: SQL Statement ignored
Any idea where I am going off the track?
Thanks in advance :-)
I think you need to provide the actual column name, rather than the string representing the column name in the statement. Would an execute immediate work for you?
declare
column_var varchar2(2048);
cursor pointer is
select column_name into column_var from user_tab_cols where table_name = 'TABLE_NAME';
begin
for x in pointer
loop
execute immediate
'update table_name ' ||
'set '|| x.column_var ||' = trim(both '' '' from '|| x.column_var ||')'||
'where '|| x.column_var ||' like ''% ''';
commit;
end loop;
end;
You need to use dynamic sql for this
BEGIN
FOR t IN (select column_name from user_tab_cols where table_name = 'MyTable')
LOOP
EXECUTE IMMEDIATE
'update MyTable set '||t.column_name||' = trim(both '' '' from '||t.column_name||') where '||t.column_name||' like ''% ''';
END LOOP;
END;
/

pl/sql get all structure tables on oracle

I have problem about how to get all of list structure table using loop pl/sql,
i have syntax loop like bellow :
BEGIN
FOR x IN (SELECT table_name FROM user_tables)
LOOP
EXECUTE IMMEDIATE 'DESC' || x.table_name;
END LOOP;
COMMIT;
END;
and this is log output from sql developer
Error starting at line 14 in command:
BEGIN
FOR x IN (SELECT table_name FROM user_tables)
LOOP
EXECUTE IMMEDIATE 'DESC' || x.table_name;
END LOOP;
COMMIT;
END;
Error report:
ORA-00900: invalid SQL statement
ORA-06512: at line 4
00900. 00000 - "invalid SQL statement"
*Cause:
*Action:
Help me to solve this
Thanks
The DESC command is neither SQL nor plsql but a sqlplus command, so you can't use it in a plsql code.
More than that, why ? you can get all the values you need from USER_TAB_COLUMNS ...
Use DBMS_OUTPUT.put_line() instead:
BEGIN
FOR x IN (SELECT COLUMN_NAME, DATA_TYPE, NULLABLE FROM USER_TAB_COLUMNS)
LOOP
DBMS_OUTPUT.put_line(COLUMN_NAME || ' ' || DATA_TYPE || ' ' || NULLABLE );
END LOOP;
COMMIT;
END;
Of course this can be formatted nicer and there are more columns you can select ...
Consider this instead:
Select * from All_tab_Cols
or to be closer to what is returned from describe:
Select Owner, Table_name, ColumN_Name, Data_Type, Data_Length, Nullable
from all_tab_Cols;
To address your specific problem shouldn't it be:
EXECUTE IMMEDIATE 'DESC ' || x.table_name|| ';';
Missing space between desc & table name and ; is part of the SQL to run isn't it?
If you're trying to reverse engineer the schema then use the DBMS_Metadata package to do so, as it can also extract constraints, privileges, users etc.

Resources