Oracle: IN function within an IF block - sql

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

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

Print multiple rows from select statement

I have this table :
| Pattern |
----------------------
|category |varchar|
|patternexpr |varchar|
For example in this table I can have a category ISBN and its pattern to recognize it.
I want to create a procedure which takes three arguments : a table T, one of its column C and a category. I want to print every rows in column C in T table which respect the pattern associated.
This is what I did (Updated with the correct answer):
CREATE OR REPLACE PROCEDURE Recognize(T varchar,C varchar,catego varchar)
IS
v_patt Pattern.CATEGOR%Type;
BEGIN
SELECT patternexpr INTO v_patt
FROM Pattern WHERE CATEGOR=catego;
FOR myrow IN (SELECT C FROM T WHERE REGEXP_LIKE(C, v_patt) LOOP
dbms_output.put_line(myrow.C);
END LOOP;
END;
/
How can I declare a cursor to print my result without knowing the value of my variable patt in the "DECLARE" place ? Should I add another declare and begin...end bloc after the first query ? What is the best way to do it ?
(I'm working on Oracle SGBD)
Use REF CURSOR to fetch records for this purpose.
CREATE OR REPLACE PROCEDURE Recognize(
T VARCHAR2,
C VARCHAR2,
catego VARCHAR2)
IS
v_patt Pattern.CATEGOR%Type;
v_cur_txt VARCHAR2(400);
TYPE cur_type
IS
REF
CURSOR;
v_cur cur_type;
v_c VARCHAR2(20);
BEGIN
SELECT patternexpr INTO v_patt FROM Pattern WHERE CATEGOR=catego;
v_cur_txt := 'SELECT '||C||' FROM '|| T ||' WHERE REGEXP_LIKE('||C||', '''||v_patt||''')';
OPEN v_cur FOR v_cur_txt;
LOOP
FETCH v_cur INTO v_c;
EXIT
WHEN v_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_c);
END LOOP;
CLOSE v_cur;
END;
/
NOTE: : Include proper EXCEPTION handling in your code for NO_DATA_FOUND etc.Also as per Nicholas , make some validations by using dbms_assert package
In Oracle, you don't need an explicit cursor:
for myrow in (select c from t where regexp_like(c, patt) loop
dbms_output.put_line(myrow.c);
end loop;
I would call the pattern variable something like v_patt; that way, declared variables don't get confused with column names.

Novice to DB - Oracle

I'm pretty new to Oracle and Database.
I'm trying to write a stored procedure with cursors. How do I write a select statement inside the cursor loop, and how do I loop through the result set that I get from the select inside that cursor loop?
For example:
Open Curs1;
Exit When Curs1%NotFound;
Loop
Select column1,column2 from table -- Returns multiple records. How to loop through this record set and perform CRUD operations.
End loop;
Close Curs1;
Use a FOR-loop cursor - they are faster and simpler than the open/fetch/close syntax.
begin
for results1 in
(
select ...
) loop
--Do something here
for results2 in
(
select ...
where some_table.some_column = results1.some_column
) loop
--Do something here
end loop;
end loop;
end;
/
Although this literally answers the question, you generally do not want to have loops inside loops like this. If possible, it would be better to combine the two SQL statements with a join and then loop through the results.
Try to use the following example that fetches rows one at a time from the cursor variable emp_cv into the user-defined record emp_rec:
declare
TYPE YourType IS ref cursor return YourTable%rowtype;
tab_cv YourType;
tab_rec YourTable%rowtype;
begin
loop
fetch tab_cv into emp_rec;
exit when tab_cv%notfound;
...
end loop;
end;
The BULK COLLECT clause lets you fetch entire columns from the result set, or the entire result set at once. The following example, retrieves columns from a cursor into a collection:
declare
type NameList IS table of emp.ename%type;
names NameList;
cursor c1 is select ename from emp where job = 'CLERK';
begin
open c1;
fetch c1 bulk collect into names;
...
close c1;
end;
The following example uses the LIMIT clause. With each iteration of the loop, the FETCH statement fetches 100 rows (or less) into index-by table acct_ids. The previous values are overwritten.
declare
type NumList is table of number index by binary_integer;
cursor c1 is select acct_id from accounts;
acct_ids NumList;
rows natural := 100; -- set limit
begin
open c1;
loop
/* The following statement fetches 100 rows (or less). */
fetch c1 bulk collect into acct_ids limit rows;
exit when c1%notfound;
...
end loop;
close c1;
end;
You need to declare the CURSOR and FETCH the records in the loop.
DECLARE
CURSOR curs1
IS
SELECT column1,
column2
FROM yourtable;
v_column1 yourtable.column1%TYPE;
v_column2 yourtable.column2%TYPE;
BEGIN
OPEN curs1;
LOOP
FETCH curs1
INTO v_column1,
v_column2;
EXIT
WHEN curs1%NOTFOUND;
INSERT INTO yourtable2(col1)VALUES( '000'||v_column1 );
-- A sample DML operation.
--Do other operations on individual records here.
END LOOP;
CLOSE curs1;
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.

PL/SQL cursor with IF condition

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;