Oracle pl/sql reference(return?) cursor/declared cursor - sql

Is there a way to do something like this? Output a cursor's fetched data into a refcursor without stuffing it into a table first?
create or replace procedure someprocedure
(
rc1 in out adv_refcur_pkg.rc) -- this is defined below
as
v_class_year varchar(10);
cursor c1
is
select distinct e.pref_class_year
from entity e
where e.pref_class_year between i_start_class and i_end_class;
begin
open c1;
loop
fetch c1 into v_class_year;
EXIT WHEN c1%NOTFOUND;
end loop;
close c1;
open rc1 for select v_class_year from dual;
end;
here is the refcursor's declaration
CREATE OR REPLACE PACKAGE ADVANCE.adv_refcur_pkg
AS
TYPE RC IS REF CURSOR;
END adv_refcur_pkg;

According with this example, yes, it's possible:
https://forums.oracle.com/thread/696634

Why go to the trouble of doing that when you can simply pass the cursor itself?
create or replace procedure someprocedure
(
rc1 in out adv_refcur_pkg.rc) -- this is defined below
as
begin
open rc1 for
select distinct e.pref_class_year
from entity e
where e.pref_class_year between i_start_class and i_end_class;
end;
When you call "someprocedure", you have an open cursor you can then fetch from:
BEGIN
...
someprocedure(TheCursor);
LOOP
fetch TheCursor into v_class_year;
exit when TheCursor%NOTFOUND;
...
END LOOP;
CLOSE TheCursor;
...
END;

Related

Oracle procedure with SYS_REFCURSOR as OUT parameter displays only old values

I created the following table called APP_USERS with nine columns:
I want to get all data from a single record by calling a procedure with SYS_REFCURSOR as OUT parameter: The code looks like that:
create or replace PACKAGE BODY USER_LOGIN AS
PROCEDURE getUserToLogIn(comp_id NUMBER, user_id APP_USERS.USER_ID%TYPE, pass APP_USERS.ACC_PASSWORD%TYPE, v_IS_ADMIN IN char,
curs out sys_refcursor)
IS
lala APP_USERS%ROWTYPE;
BEGIN
OPEN curs FOR
SELECT USER_ID,FIRST_NAME,MIDDLE_NAME,LAST_NAME,SEX,COMPANY_ID,IS_ADMIN,ACC_PASSWORD,IS_ACTIVE
FROM APP_USERS
WHERE COMPANY_ID = comp_id and USER_ID = TRIM(user_id) and ACC_PASSWORD = pass and IS_ADMIN = v_IS_ADMIN;
DBMS_OUTPUT.PUT_LINE(comp_id);
DBMS_OUTPUT.PUT_LINE(user_id);
DBMS_OUTPUT.PUT_LINE(pass);
DBMS_OUTPUT.PUT_LINE(v_IS_ADMIN);
DBMS_OUTPUT.PUT_LINE('--------------------------------------');
LOOP
FETCH curs INTO lala;
exit when curs%notfound;
DBMS_OUTPUT.PUT_LINE(lala.USER_ID);
DBMS_OUTPUT.PUT_LINE(lala.COMPANY_ID);
DBMS_OUTPUT.PUT_LINE(lala.ACC_PASSWORD);
DBMS_OUTPUT.PUT_LINE(lala.IS_ADMIN);
END LOOP;
CLOSE curs;
END getUserToLogIn;
END USER_LOGIN;
At first I called a procedure by running a simple anonymous block like below:
declare
curs sys_refcursor;
begin
USER_LOGIN.getUserToLogIn(1000,'EU1002','UCFE3M0N','N',curs);
end;
and I got the following results:
Next time I run the anonymous block again with wrong data, so that the procedure could not find them, but the result was not correct:
At first time, when I entered correct data my procedure displayed them, but the second attempt with wrong data entered caused displaying the data from the previous one.
My question is why:
exit when curs%notfound;
is this part of the code overlooked?
That not the proper way to use a refcursor. Here is a simplified example from your code:
create or replace PROCEDURE getUserToLogIn( user_id APP_USERS.USER_ID%TYPE,
curs in out sys_refcursor)
IS
BEGIN
OPEN curs FOR SELECT * FROM APP_USERS; -- where blah, blah
end ;
declare
lala APP_USERS%ROWTYPE;
my_cursor sys_refcursor;
begin
getUserToLogIn('xxx', my_cursor);
LOOP
FETCH my_cursor INTO lala;
exit when my_cursor%notfound;
DBMS_OUTPUT.PUT_LINE('output= ' || lala.USER_ID);
end loop;
close my_cursor;
end;
So the standard method is you call the procedure that creates the SELECT statement you need, then just OPEN cursor parameter. The caller then gets the cursor back, iterates thought it, then closes it.
Heres also a tutorial.

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;
/

How to execute results of dbms_output.put_line

There is a table contains this kind of data: select to_char(sysdate,'day') from dual in a column. I want to get results of the every query that the table keeps.
My result set should be the result of select to_char(sysdate,'day') from dual query. So in this case it is a tuesday.
SO_SQL_BODY is Varchar2.
I wrote this code but it returns only table data.
CREATE or replace PROCEDURE a_proc
AS
CURSOR var_cur IS
select SO_SQL_BODY FROM SO_SUB_VARS group by SO_SQL_BODY;
var_t var_cur%ROWTYPE;
TYPE var_ntt IS TABLE OF var_t%TYPE;
var_names var_ntt;
BEGIN
OPEN var_cur;
FETCH var_cur BULK COLLECT INTO var_names;
CLOSE var_cur;
FOR indx IN 1..var_names.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(var_names(indx).SO_SQL_BODY);
END LOOP;
END a_proc;
DECLARE
res varchar2(4000);
sql_str varchar2(1000);
BEGIN
FOR r IN
(select SO_SQL_BODY FROM SO_SUB_VARS WHERE SO_SQL_BODY IS NOT NULL
)
LOOP
sql_str := r.SO_SQL_BODY;
EXECUTE immediate sql_str INTO res;
dbms_output.put_line(sql_str);
dbms_output.put_line('***********************');
dbms_output.put_line(res);
dbms_output.put_line('***********************');
END LOOP;
END;
/
Try this - iterate to not null records - execute them and print the result.This script works supposing the fact that SO_SQL_BODY contains a query which projects only one column.Also if the projection is with more than two columns then try to use a refcursor and dbms_sql package
İf var_names(indx).SO_SQL_BODY output is a runnable sql text;
CREATE or replace PROCEDURE a_proc
AS
CURSOR var_cur IS
select SO_SQL_BODY FROM SO_SUB_VARS group by SO_SQL_BODY;
var_t var_cur%ROWTYPE;
TYPE var_ntt IS TABLE OF var_t%TYPE;
var_names var_ntt;
BEGIN
OPEN var_cur;
FETCH var_cur BULK COLLECT INTO var_names;
CLOSE var_cur;
FOR indx IN 1..var_names.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(var_names(indx).SO_SQL_BODY);
EXECUTE IMMEDIATE var_names(indx).SO_SQL_BODY;
END LOOP;
END a_proc;
You don't need a full cursor for this example. An implicit one would make it a lot shorter.
create or replace procedure a_proc is
lReturnValue varchar2(250);
begin
for q in (select so_sql_body from so_sub_vars group by so_sql_body)
loop
execute immediate q.so_sql_body into lReturnValue;
dbms_output.put_line(lReturnValue);
end loop;
end a_proc;
You should add an exception handler that will care for cases where there is a bad SQL query in your table. Also note that executing querys saved in a database table is your entry point to SQL injection.

How to display table from returned cursor in Oracle?

I need to get a table of bank's name which their bsb is equal to value given to the function.
Here is my code:
CREATE OR REPLACE Function FF(BSB_NUMBER IN BANK.BSB#%TYPE) RETURN SYS_REFCURSOR
IS
MY_CURSOR SYS_REFCURSOR;
BEGIN
OPEN MY_CURSOR for
select * from bank where bank.bsb# = BSB_NUMBER;
return MY_CURSOR;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Error ! There is no such account');
END FF;
/
And I run in with this but doesn't print anything. Appreciate any idea:
SELECT FindBankStaff(012878) FROM BANK;
Don't return the cursor, return the appropriate value.
Finally I saw this answer and implement it to my issue:
CREATE OR REPLACE FUNCTION SOME_FUNC_RETURNING_A_CURSOR RETURN SYS_REFCURSOR IS
csrLocal SYS_REFCURSOR;
BEGIN
OPEN csrLocal FOR SELECT NAME, BSB# FROM BANK;
RETURN csrLocal;
END SOME_FUNC_RETURNING_A_CURSOR;
/
DECLARE
aCursor SYS_REFCURSOR;
someVariable VARCHAR2(40);
some2 number;
BEGIN
aCursor := SOME_FUNC_RETURNING_A_CURSOR;
WHILE TRUE LOOP
FETCH aCursor INTO someVariable,some2;
EXIT WHEN aCursor%NOTFOUND;
DBMS_OUTPUT.PUT(someVariable);
DBMS_OUTPUT.PUT(' ');
DBMS_OUTPUT.PUT_LINE(some2);
END LOOP;
COMMIT;
END;
/
In Toad there is an option to do this.
You just have to create a bind variable as Ref cursor and invoke the procedure or function. Once the process runs successfully, the Data Grid is automatically populated with the result set of the Object (In your case the records from ref cursor). I suppose even in Oracle Sql Developer the Feature has been incorporated.
If you are using SQL Plus* then the best way is to declare a variable as ref cursor. Execute the piece of block and print the output using print command. Hope it helps...

Print first 100 values using cursor in PL/SQL

I am trying to print the first 100 values for a field using cursor.I get an ORA-06550 error here though.Could someone tell me what is that I am missing .
Declare
BG_TOTAL number;
cursor c1 is
select BG_ID
from <tablename>;
Type BG_TAB_TYPE is table of c1%ROWTYPE;
BG_LIST BG_TAB_TYPE;
Begin
open c1;
FETCH c1 BULK COLLECT INTO BG_LIST;
close c1;
for i in 1..c1.count
loop
DBMS_OUTPUT.PUT_LINE(c1(i).BG_ID);
End loop;
end;
Yet another option is to use bulk collect with a limit.
This nicely separates the content, the limitation and the processing. The separation might not be an issue in your case but I have found this useful every now an then (helps me to write more modular code that's easy to test).
declare
-- data content
cursor tables_c is select * from all_tables;
type table_list_t is table of tables_c%rowtype;
v_table_list table_list_t;
begin
open tables_c;
-- limiting the data set
fetch tables_c bulk collect into v_table_list limit 8;
-- processing
for i in 1 .. v_table_list.count loop
dbms_output.put_line(v_table_list(i).table_name);
end loop;
close tables_c;
end;
/
You should loop through the nested table to which you bulk-collected records, not through the cursor. This is the corrected code:
Declare
BG_TOTAL number;
cursor c1 is
select BG_ID
from my_Tab524;
Type BG_TAB_TYPE is table of c1%ROWTYPE;
BG_LIST BG_TAB_TYPE;
Begin
open c1;
FETCH c1 BULK COLLECT INTO BG_LIST;
close c1;
for i in 1..BG_LIST.count
loop
DBMS_OUTPUT.PUT_LINE(BG_LIST(i).BG_ID);
EXIT WHEN i = 10;
End loop;
end;