PL/SQL cursor with IF condition - sql

I Have below cursor in the code.
CURSOR cur1
IS
SELECT a, b, c, d,
FROM EMP;
BEGIN
--Stored procedure logic
END
This curosr is getting information from EMP table.
But I need to change is as per below
There is a table (Table1) with Key Value pairs.
If the Table1 value is TRUE then the cursor should be created with STUDENT table
If the table1 value is FALSE then the cursor should be created with EMP table.
I can check the Value in the Table1 as below
select t.value into variable1 from Table1 t where s.key='xxxxx';
And I want write something like
IF variable1 := 'true'
curosr created with STUDENT
ELSE
curosr created with EMP
END IF
BEGIN
--Stored procedure logic
END
How to do it?

In another way you can just keep two CURSORS for those two scenarios and OPEN them on the condition. Declaring two CURSORS will not affect to the performance; you should be careful when OPEN a CURSOR and FETCHING from it.
PROCEDURE Get_Details_On_Condition ( name_ OUT VARCHAR2, isEmp IN BOOLEAN )
IS
CURSOR get_emp IS
SELECT name
FROM EMP;
CURSOR get_std IS
SELECT name
FROM STUDENT;
BEGIN
IF isEmp THEN
OPEN get_emp ;
FETCH get_emp INTO name_ ;
CLOSE get_emp ;
ELSE
OPEN get_std ;
FETCH get_std INTO name_ ;
CLOSE get_std ;
END IF;
RETURN name_;
END Get_Details_On_Condition;

Using if .. else construct is not proper (neither supported). You can use REF cursor to achieve the same like below.
DECLARE type cur1 REF CURSOR;
c1 cur1;
BEGIN
IF (variable1 := 'true') THEN
OPEN c1 FOR 'SELECT * FROM STUDENT';
ELSE
OPEN c1 FOR 'SELECT * FORM EMP';
END IF ;
END;
Idea taken from Oracle Community Forum Post
NOTE: I didn't included the entire code block (I mean cursor processing, closing etc) cause the main concern here is "How he will declare/define conditional cursor". So, pointed that particular in my code snippet. Since, rest of the part like processing the cursor and closing can be directly be found in Oracle specification.
For a complete code block, you can refer the answer given by Harsh

I would prefer to solve this without using dynamic SQL. If the code to process the results is the same for both tables, then it is reasonable to assume that the columns are the same (or equivalent) as well. My inclination would be to solve this using UNION and sub-queries:
DECLARE
CURSOR cur1 IS
SELECT a, b, c, d
FROM emp
WHERE NOT EXISTS
(SELECT *
FROM table1
WHERE s.key = 'xxxxx' AND t.VALUE = 'true')
UNION ALL
SELECT a, b, c, d
FROM student
WHERE EXISTS
(SELECT *
FROM table1
WHERE s.key = 'xxxxx' AND t.VALUE = 'true');
BEGIN
--Stored procedure logic
END;

The link provided by Rahul indicates the correct way to solve the problem. From the Oracle community forum post posted by Rahul, I have taken the code snippet through which the code could run successfully.
Rahul: Please do not take this as a redundant answer as I could not comment on your answer to help shyam to take the code snippet in the link posted by you.
Declare
TYPE cv_typ IS REF CURSOR;
cv cv_typ;
Begin
If(condition1 is TRUE) then
open cv FOR
'Select * from table_name1';
EXIT WHEN cv%NOTFOUND;
ELSE
open cv FOR
'Select * from table_name2';
EXIT WHEN cv%NOTFOUND;
End If;
CLOSE cv;
END;
Thanks & Regards,
Harsh

You can move the OPEN outside IF statement:
DECLARE type cur1 REF CURSOR;
c1 cur1;
vSQL VARCHAR2(1000);
BEGIN
IF (variable1 = 'true') THEN
vSQL := 'SELECT * FROM STUDENT';
ELSE
vSQL := 'SELECT * FORM EMP';
END IF;
OPEN c1 FOR vSQL;
--procedure logic
CLOSE c1;
END;

Related

finding records with cursors and procedures

i dont kno whow to write a proper procedure wtih cursors. i am doing somehtings wring ?
procedure1: for given student show all titles of books he ever rented
--no such student no recordsexcep
SET SERVEROUTPUT ON
create or replace procedure showRENTS(v_idStudent int)
as
v_NameOfBook varchar2(100);
--v_count int;
no_such_id EXCEPTION;
cursor c1 is
select NameOfbook from book
where
begin
open c1;
loop
fetch c1
into v_fname,v_lname;
--select count(1) into v_count from Student where idStduent =v_id ;
--if
--v_count = 0;
--then raise no_such_id;
--end if;
exit when c1%notfounf
end loop;
close c1;
end;
/
I can't see images, but - procedure you posted can be rewritten to
CREATE OR REPLACE PROCEDURE showrents (v_idstudent IN INT)
AS
BEGIN
FOR c1 IN (SELECT nameofbook FROM book)
LOOP
DBMS_OUTPUT.put_line (c1.nameofbook);
END LOOP;
END;
/
I'd suggest you to use cursor FOR loop - it is simpler to write & maintain as Oracle does most of job for you (you don't have to declare cursor variable, open the cursor, fetch from it, take care about exiting the loop, closing the cursor).
I'm not sure what its parameter has to do with it but I left it; I guess you'll add some code later.
I hope it'll get you started.
For issues like this just follow your ERD. So here your given a student is and asked for the book rented. Follow the path: Student -> Process -> Copies -> Book. It actually becomes a fairly easy query.
create or replace procedure showrents(v_idStudent int)
as
no_such_id EXCEPTION;
v_student_ok interger :=0;
cursor c_student_books is
select nameofbook
from student s
join process p on (p.idstudent = s.idstudent)
join copies c on (c.idcopies = p.idcopies)
join books b on (b.idbook = b.idbook)
where s.idstudent = v_idStudent;
begin
select count(*)
into v_student_ok
from student
where idstudent = v_idstudent
and rownum <= 1;
if v_student_ok = 0
then
raise no_such_id;
end if;
for rented in c_student_books
loop
dbms_output.put_line( rented.nameofBook);
end ;
end showrents; ```

PL/SQL Procedure Use String Select Statement in for loop

I am building a procedure, where I`m first creating a select statement and store it in an VARCAHR variable.
I now want to execute that query and store the whole result set in an variable to loop through it or use directly in a for loop.
I only find examples where the Select is hard written in the for loop definition.
How do i exchange the Select statement with my variable that holds my select statement?
for r IN (SELECT ... FROM ...)
loop
--do sth;
end loop;
how i want to use it :
statement := 'SELECT .... FROM ...';
for r IN (statement) -- HOW TO DO THIS
loop
--do sth;
end loop;
For a dynamic ref cursor, you need to define everything explicitly:
declare
sqlstring long := 'select 123 as id, ''demo'' as somevalue from dual where dummy = :b1';
resultset sys_refcursor;
type demo_rectype is record
( id integer
, somevalue varchar2(30) );
demorec demo_rectype;
begin
open resultset for sqlstring using 'X';
loop
fetch resultset into demorec;
exit when resultset%notfound;
dbms_output.put_line('id=' || demorec.id || ' somevalue=' || demorec.somevalue);
end loop;
close resultset;
end;
You can parse the cursor and figure out the column names and datatypes with DBMS_SQL. Example here: www.williamrobertson.net/documents/refcursor-to-csv.shtml

DB2 dynamic table name

I want to count the rows of a number of tables. But the table name should be used dynamically. I want to do that within one SQL statement.
I tried it with
BEGIN ATOMIC
FOR tmp AS (
SELECT tabschema || '.' || tabname tbl
FROM syscat.tables WHERE tabname LIKE '%CD') DO
(SELECT COUNT(*) FROM tmp.tbl);
END FOR;
END
but I receive the error
DB21034E The command was processed as an SQL statement because it was not a
valid Command Line Processor command. During SQL processing it returned:
SQL0204N "TMP.TBL" is an undefined name. LINE NUMBER=1. SQLSTATE=42704
and found no other working solution...
Is there a solution for that?
Thanks in advance.
I assume that your SELECT COUNT(*) FROM tmp.tbl should translate in multiple statements like
select count(*) from TABLECD
select count(*) from TABLE2CD
...
However, your query will try to do a count of the table TBL in the schema TMP.
You'll have to prepare the complete SQL statement, store it in a variable and pass it to the PREPARE statement (documentation ).
A rather complete stored procedure which somewhat fits your requirements can be found here . The result of the counts will be stored in a table COUNTERS which you can query afterwards.
//edit: this is the example from the topic, adapt to work (not tested since I have no DB2 instance to test atm):
CREATE PROCEDURE tableCount()
LANGUAGE SQL
BEGIN
DECLARE SQLCODE INTEGER DEFAULT 0;
DECLARE SQLSTATE CHAR(5);
DECLARE vTableName VARCHAR(20);
DECLARE vTableCount INTEGER;
DECLARE stmt varchar(2000);
DECLARE not_found CONDITION FOR SQLSTATE '02000';
DECLARE c1 CURSOR FOR
SELECT tabname from syscat.tables where tabschema='DB2ADMIN';
DECLARE C2 CURSOR FOR S2
DECLARE CONTINUE HANDLER FOR not_found
SET stmt = '';
Delete from COUNTERS;
OPEN c1;
getRows:
LOOP
FETCH c1 INTO vTableName;
IF SQLCODE = 0 THEN
SET stmt ='SELECT Count(*) FROM ' || vTableName;
PREPARE S2 FROM stmt;
OPEN C2;
SET vTableCount = 0;
FETCH C2 INTO vTableCount;
INSERT INTO COUNTERS (tableName, tableCount)
VALUES (vTableName, vTableCount);
CLOSE C2;
ELSE
LEAVE getRows;
END IF;
END LOOP getRows;
CLOSE c1;
END

Oracle: IN function within an IF block

I have a cursor c2, with one column 'note', containing numbers.
I want to check, whether the column contains 5 and change my local variable if so.
this:
CREATE OR REPLACE PROCEDURE proc
IS
result varchar(50);
cursor c2 is
SELECT note
FROM student;
BEGIN
IF c2.note IN(5) THEN
result := 'contains 5';
DBMS_OUTPUT.PUT_LINE(result);
END;
/
doesnt work.
please help!
In your code, you're declaring a cursor but you are never opening it and never fetching data from it. You'd need, presumably, some sort of loop to iterate through the rows that the cursor returned. You'll either need to explicitly or implicitly declare a record into which each particular row is fetched. One option to do that is something like
CREATE OR REPLACE PROCEDURE proc
IS
result varchar(50);
cursor c2 is
SELECT note
FROM student;
BEGIN
FOR rec IN c2
LOOP
IF rec.note IN(5) THEN
result := 'contains 5';
DBMS_OUTPUT.PUT_LINE(result);
END IF;
END LOOP;
END;
/
Note that you also have to have an END IF that corresponds to your IF statement. Naming a cursor c2 is also generally a bad idea-- your variables really ought to be named meaningfully.
You have too loop over the records/rows returned in the cursor; you can't refer to the cursor itself like that:
CREATE OR REPLACE PROCEDURE proc
IS
result varchar(50);
cursor c2 is
SELECT note
FROM student;
BEGIN
FOR r2 IN c2 LOOP
IF r2.note = 5 THEN -- IN would also work but doesn't add anything
result := 'contains 5';
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE(result);
END;
/
But if you're just testing for the existence of any record with value 5 then you don't need a cursor:
CREATE OR REPLACE PROCEDURE proc
IS
result varchar(50);
BEGIN
SELECT max('contains 5')
INTO result
FROM student
WHERE note = 5;
DBMS_OUTPUT.PUT_LINE(result);
END;
/
If there are any rows with five you'll get the 'contains 5' string; if not you'll get null. The max() stops an exception being thrown if there are either zero or more than one matching records in the table.
You are missing an END IF and need to LOOP over the cursor. The procedure name is included in the final END.
But using IN should work. Like this:
CREATE OR REPLACE PROCEDURE proc
IS
result varchar(50);
cursor c2 is
SELECT note
FROM student;
BEGIN
FOR rec in c2
LOOP
IF rec.note IN (5) THEN
result := 'contains 5';
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE(result);
END proc;
/

oracle cursor value vary depending on condition

I would like to ask if it is possible to assign a variable on a cursor declaration.
CURSOR cur_name IS <variable_name>
What I would like to accomplish is that in a cursor some of where clause and the from clause of the select statement varies depending on the result of another select. Like below:
select count(*) from table_name
v_cnt
where cond1;
IF v_cnt is 0, cursor would be:
cursor cur_name IS
select * from tab_name1
where cond1;
IF v_cnt > 0, cursor would be:
cursor cur_name IS
select * from tab_name2
where cond1
and cond2;
I was wondering if I can do an if-else and then concat on the select that would be assign on the cursor.
cursor cur_name IS
select * from tab_name
if v_cnt > 0
where cond2;
else
where cond1;
Let me know if you need more details.
Appreciate any feedback.
Why not use something like
select *
from tab_name
WHERE (v_cnt = 0 AND cond1)
OR (v_cnt > 0 AND cond2)
Are you looking for something like this ?
DECLARE
V_CNT VARCHAR2(20);
CURSOR C1
IS
SELECT * from Tab1;
CURSOR C2
IS
SELECT * from Tab2;
BEGIN
SELECT COUNT(*) INTO V_CNT FROM Table_Name;
IF V_CNT > 0 THEN
OPEN C1;
--code
Close C1;
ELSE
OPEN C2;
--code
CLOSE C2;
END IF;
END;
If the cursor is very dynamic use something like:
declare
c sys_refcursor;
<here declare the record you would like fetch results to>
begin
open c for 'you query in quotes as the string that you created before regarding your conditions';
loop
FETCH c INTO your record;
EXIT WHEN c%NOTFOUND;
end loop;
end;
At any case have a look into http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/sqloperations.htm#BABFEJED. It describes your case by my opinion.
There are a lot of ways to do this, and there are many trade-offs involved.
Alexander Tokarev's answer is the most flexible. But dynamic SQL can be tricky, dependency problems won't show up at compile time, etc. Balaji Sukumaran's answer is less flexible, but simpler, and breaks the code into smaller chunks.
If the columns selected will always be the same, you can use a method like this:
cursor cur_name(v_cnt number) is
select *
from tab_name1
where 1=1 /*condition 1*/
and v_cnt > 0
---------
union all
---------
select *
from tab_name2
where 2=2 /*condition 2*/
and (v_cnt is null or v_cnt <= 0);
It puts everything together, which can be more confusing than Balaji's answer. But sometimes it's better to have all the logic in a single SQL. It may help reduce repeating logic.
(Also, you don't need to worry about Oracle actually using both queries, and running slowly. It's smart enough to know that there's a bind variable that controls which query is used. That's what the FILTER step in the explain plan does.)