How to declare a cursor if table used in select statement may not exist sometimes? - sql

I need to run below code on different environments and it works fine when table1 exists but when it does not exist then it throws error in cursor declaration that "table or view does not exist".
I am running this on Oracle.
Could you please help me in correcting this?
Thanks in advance.
DECLARE
CURSOR my_cursor IS (select "col1" from "table1");
name1 VARCHAR2(256);
tableCount NUMBER;
BEGIN
Select count(*) into tableCount from user_tab_cols where table_name = 'table1' and column_name = 'col2';
IF tableCount > 0 THEN
OPEN my_cursor;
LOOP
FETCH my_cursor into name1;
EXIT WHEN my_cursor%notfound;
-- Update or delete statement here
DBMS_OUTPUT.PUT_LINE('value is ' || name1);
END LOOP;
CLOSE my_cursor;
END IF;
END;
/

A CURSOR with a defined return type is strongly typed. Sys_refcursors are weakly typed. This means that any return type in a CURSOR must be valid. A SYS_REFCURSOR is more flexible and can be defined at a later time.
When setting a CURSOR in the declaration section, you must use a SQL statement that will execute properly. In this case, you are setting the CURSOR to select a column from a table that does not exist. The database execution will not reach the body of the code since it errors prior to exiting the declaration block.
To fix this, use a SYS_REFCURSOR with a dynamic sql query, as mentioned by Tejash above. This allows you to check to see if the table exists before setting the cursor. If the table exists, set the cursor to select from the specified table. If it doesn't, output a message saying that it does not exist.
Note that you can also use the SQL error codes, as shown in other answers. I prefer personally to handle business rules in the logic prior to the error occurring.
DECLARE
my_cursor sys_refcursor;
name1 VARCHAR2(256);
tableCount NUMBER;
BEGIN
Select count(*) into tableCount from user_tab_cols where table_name = 'table1' and column_name = 'col2';
IF tableCount > 0 THEN
OPEN my_cursor for 'select order_id from table1';
LOOP
FETCH my_cursor into name1;
EXIT WHEN my_cursor%notfound;
-- Update or delete statement here
DBMS_OUTPUT.PUT_LINE('value is ' || name1);
END LOOP;
CLOSE my_cursor;
else
dbms_output.put_line('Table does not exist');
END IF;
END;
http://docs.oracle.com/database/122/LNPLS/static-sql.htm#LNPLS568

You can use the exceptions and dynamic string for a cursor as follows:
DECLARE
MY_CURSOR SYS_REFCURSOR;
NAME1 VARCHAR2(256);
TABLECOUNT NUMBER;
BEGIN
OPEN MY_CURSOR FOR 'SELECT ACC_NR FROM ACCOUNT'; -- dynamic string for cursor
LOOP
FETCH MY_CURSOR INTO NAME1;
EXIT WHEN MY_CURSOR%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('value is ' || NAME1);
END LOOP;
CLOSE MY_CURSOR;
EXCEPTION -- exception handling using SQLCODE
WHEN OTHERS THEN
IF SQLCODE = -942 THEN
DBMS_OUTPUT.PUT_LINE('Table does not exists');
ELSIF SQLCODE = -904 THEN
DBMS_OUTPUT.PUT_LINE('Invalid column name');
ELSE
DBMS_OUTPUT.PUT_LINE('Other error');
END IF;
END;
/

Related

how to check if SYS_REFCURSOR is empty

I have a general SYS_REFCURSOR that I want to check if it is empty or not.
The code is like this:
declare
v_cursor SYS_REFCURSOR;
begin
OPEN v_cursor FOR <any select statement>.
check if v_cursor is empty.
end;
Can someone tell me how to check if the weak cursor is empty, please?
I have to mention that the base SELECT statement can be anything from any table.
The column numbers or type it is known only at runtime.
Thank you,
You can't see if a ref cursor contains data without fetching, and consuming at least one row from it.
If you really need to determine this at the point the cursor is opened, without knowing the structure at that point, you could execute a modified query that just counts the rows returned by your real query - possibly limited to a single row if that helps performance - either as a simple execute immediate ... into ... or with a separate open/fetch/close for consistency; something like:
declare
v_cursor SYS_REFCURSOR;
v_query VARCHAR2(4000);
v_count PLS_INTEGER;
begin
v_query := <any select statement>;
-- see if the query finds any data
OPEN v_cursor FOR 'select count(*) from (' || v_query || ')'; -- could limit rows
FETCH v_cursor INTO v_count;
CLOSE v_cursor;
if v_count = 0 then
dbms_output.put_line('No data');
return;
end if;
dbms_output.put_line('Found data, opening cursor for real');
OPEN v_cursor FOR v_query;
-- loop over results, return to caller, etc.
end;
/
db<>fiddle
Try to fetch a row and then use v_cursor%NOTFOUND to determine of the cursor is empty:
DECLARE
v_cursor SYS_REFCURSOR;
v_value DUAL.DUMMY%TYPE;
BEGIN
OPEN v_cursor FOR SELECT DUMMY FROM DUAL WHERE 1 = 0;
FETCH v_cursor INTO v_value;
IF v_cursor%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Cursor empty');
ELSE
DBMS_OUTPUT.PUT_LINE('Cursor not empty');
END IF;
END;
/
or
DECLARE
CURSOR v_cursor IS
SELECT DUMMY FROM DUAL WHERE 1 = 0;
v_row v_cursor%ROWTYPE;
BEGIN
OPEN v_cursor;
FETCH v_cursor INTO v_row;
IF v_cursor%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Cursor empty');
ELSE
DBMS_OUTPUT.PUT_LINE('Cursor not empty');
END IF;
END;
/
Both output:
Cursor empty
db<>fiddle here

How to Create Views for all Tables in my DB

I have written the code below to correct all table in my db.
Please review and correct it.
Declare
Name Varchar2(100);
A Number:=0;
Cursor C1 Is
Select Table_Name From Tabs;
Begin
Open C1;
Loop
A:=A+1;
Fetch C1 Into Name;
Exit When C1%Notfound;
Execute Immediate 'Create Or Replace View Tab||A
As
Select * From Name';
End Loop;
Close C1;
End
Please correct my Code
This forum is not a place to give people assigments, please ask questions about the issue you are running into and we are willing to help you. Please try doing a better job next time.
Your execute immediate string should be:
Execute Immediate
'Create Or Replace View Tab' ||A|| '
As
Select * From ' || Name;
and don't forget the ; after the final end
fixed your code:
Declare
Name Varchar2(100);
A Number:=0;
Cursor C1 Is
Select Table_Name From Tabs;
Begin
Open C1;
Loop
A:=A+1;
Fetch C1 Into Name;
Exit When C1%Notfound;
Execute Immediate 'Create Or Replace View Tab'||A||'
As
Select * From "'||Name||'"';
End Loop;
Close C1;
End;
/
But I would do something like this:
declare
procedure p_exec(pCMD in varchar2, pPrintOnly varchar2 default 'n') is
begin
if pPrintOnly='y' then
dbms_output.put_line(pCMD);
else
execute immediate pCMD;
dbms_output.put_line(pCMD);
end if;
end;
begin
for r in (Select rownum rn, Table_Name From Tabs) loop
p_exec(
utl_lms.format_message(
'Create Or Replace View Tab%s As Select * From "%s"'
, to_char(r.rn,'fm9999')
, r.table_name
)
-- ,'y' -- << uncomment to print commands only without execution
);
end loop;
end;
/

Command not properly ended PL SQL

I'm trying to implement a pl sql program that export the content of all tables of a particular user in a file but i'm getting 'command not properly ended' problem whenever i run it.
here is my code:
create or replace procedure userTable(name dba_tables.owner%type)
is
cursor cur1 is select table_name from dba_tables where owner = upper(name);
cursor cur(tabName in varchar2) is select column_name from dba_tab_columns where owner = upper(name) and table_name = upper(tabName);
V$FILEP UTL_FILE.FILE_TYPE;
type dyncursor is ref cursor;
dyn dyncursor;
req varchar2(32767):='select to_char(';
lig Varchar2(32767);
i integer:=1;
begin
if existUser(name) = true then
V$FILEP := UTL_FILE.FOPEN('DIRT001','TEST09.UTL','W');
for vc1 in cur1 loop
for vc in cur(to_char(vc1.table_name)) loop
if (i=1) then
req:=req||vc.column_name||')';
i:=0;
else
req:=req||'||'||''','''||'||'||'to_char('||vc.column_name||')';
end if;
end loop;
req:=req||' from '||name||'.'||'to_char('||vc1.table_name||')';
open dyn for req;
loop
fetch dyn into lig;
exit when dyn%notfound;
dbms_output.put_line(lig);
UTL_FILE.PUT_LINE(V$FILEP,lig );
end loop;
close dyn;
end loop;
UTL_FILE.FCLOSE(V$FILEP);
else
dbms_output.put_line('user invalid');
end if;
end;
/
create or replace function existUser(name dba_users.username%type)
return boolean is
nb number :=0;
begin
select count(*) into nb from dba_users where username = upper(name);
if nb = 0 then
return false;
else
return true;
end if;
end;
/
Expanding on what has been mentioned in the comments, adding a DBMS_OUTPUT is going to show you that you have 'from schema.to_char(table_name)'.....
vc1.table_name & vc.column_name are already character (varchar2) so there is no need to do a to_char on them in the first place so just removing to_char from around all these is a good first step.
Then if it still does not work add the DBMS_OUTPUT as suggested by Arkadiusz Ɓukasiewicz in the comments to see what other issues you may have.

How can I set a cursor on a select statement with dynamic table_name in PL/SQL?

I created a PL/SQL procedure that takes the name of user and shows a list of his tables' content. The problem is that I couldn't set the cursor to accept a different table name each time and show its content.
Here is my code which is not complete yet.
set serveroutput on;
create or replace procedure userTable(name dba_tables.owner%type)
is
cursor cur is select table_name from dba_tables where owner = upper(name);
cursor cur2(tab_name dba_tables.table_name%type) is select * from name.tab_name;
V$FILEP UTL_FILE.FILE_TYPE;
begin
if existUser(name) = true then
V$FILEP := UTL_FILE.FOPEN('DIRTEST','TEST15.UTL','W');
for x in cur loop
UTL_FILE.PUT_LINE(V$FILEP,to_char(x.table_name));
end loop;
UTL_FILE.FCLOSE(V$FILEP);
else
dbms_output.put_line('user invalid');
end if;
end;
/

Dynamic SQL LOOP

Dynamic SQL is not my friend, basically the idea is that I can use the procedure with the "p_in_table" paramter to get the number of rows contained in the table.
CREATE OR REPLACE PROCEDURE how_many_rows(p_in_table VARCHAR2)
IS
TYPE cur_cur IS REF CURSOR;
v_cur_cur cur_cur;
v_rowcount NUMBER(28);
v_cur_txt VARCHAR2(299);
BEGIN
v_cur_txt := 'SELECT * FROM ' || p_in_table;
OPEN v_cur_cur FOR v_cur_txt;
LOOP
v_rowcount := v_cur_cur%ROWCOUNT;
EXIT WHEN v_cur_cur%NOTFOUND;
END LOOP;
CLOSE v_cur_cur;
dbms_output.put_line(v_rowcount);
END;
Would preciate it if someone would tell me what am I doing wrong?
The problem is that you not iterating through cursor - no fetch statement or something like that, so, basically, you have an infinite loop. To avoid this you need to do something like this:
CREATE OR REPLACE PROCEDURE how_many_rows
(p_in_table VARCHAR2) IS
TYPE cur_cur IS REF CURSOR;
v_cur_cur cur_cur;
v_rowcount NUMBER(28);
v_cur_txt VARCHAR2(299);
v_row SOME_TABLE%ROWTYPE; --add row variable
BEGIN
v_cur_txt := 'SELECT * FROM '|| p_in_table;
OPEN v_cur_cur FOR v_cur_txt;
LOOP
v_rowcount := v_cur_cur%ROWCOUNT;
FETCH v_cur_cur INTO v_row; --fetch a row in it
EXIT WHEN v_cur_cur%NOTFOUND;
END LOOP;
CLOSE v_cur_cur;
DBMS_OUTPUT.PUT_LINE(v_rowcount);
END;
But, as you can see, to do this you need to know, what table you're quering, so this is not general solution. Maybe there is a workaround for this, but i suggest, you use more simple and efficient approach, for example with EXECUTE IMMEDIATE:
CREATE OR REPLACE PROCEDURE HOW_MANY_ROWS(p_in_table VARCHAR2)
IS
v_tmp NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(1) FROM ' || p_in_table INTO v_tmp;
DBMS_OUTPUT.PUT_LINE(v_tmp);
END;
Ok, I gave a thought on how to achieve this using your way, and here is what i've ended up with - just fetch ROWNUM from your table, every table has it and you know it's type - NUMBER. So this procedure will work in general case:
CREATE OR REPLACE PROCEDURE how_many_rows
(p_in_table VARCHAR2) IS
TYPE cur_cur IS REF CURSOR;
v_cur_cur cur_cur;
v_rowcount NUMBER(28);
v_cur_txt VARCHAR2(299);
v_row NUMBER; --add rownum variable
BEGIN
v_cur_txt := 'SELECT ROWNUM FROM '|| p_in_table; --select only rownum from target table
OPEN v_cur_cur FOR v_cur_txt;
LOOP
v_rowcount := v_cur_cur%ROWCOUNT;
FETCH v_cur_cur INTO v_row; --fetch rownum in it
EXIT WHEN v_cur_cur%NOTFOUND;
END LOOP;
CLOSE v_cur_cur;
DBMS_OUTPUT.PUT_LINE(v_rowcount);
END;