I need to find a value in a cursor and do something to it, also if a value is not found, I need to perform a different action. I am using FOR LOOP cursor:
CURSOR cursor1 IS
SELECT....;
FOR c1 in cursor1 LOOP
IF c1.myvalue = l_val THEN
--the value found
--perform an action
END IF;
END LOOP;
The above works fine for when I do something with the values that are found in the cursor, but how do I capture the ones that were not found?
I think you mean, what should you do if the cursor does not return a match for l_val?
Using your method you could do this:
l_found BOOLEAN := FALSE;
CURSOR cursor1 IS
SELECT....;
FOR c1 in cursor1 LOOP
IF c1.myvalue = l_val THEN
--the value found
--perform an action
l_found := TRUE;
END IF;
END LOOP;
IF NOT l_found THEN
-- Do something when not found
END IF;
But really you do not want to be fetching all the rows in a SELECT statement and then checking each one for a match. Instead you could do the matching in the SELECT:
l_found BOOLEAN := FALSE;
CURSOR cursor1 IS
SELECT....
WHERE myvalue = l_val;
FOR c1 in cursor1 LOOP
--the value found
--perform an action
l_found := TRUE;
END LOOP;
IF NOT l_found THEN
-- Do something when not found
END IF;
Related
create or replace FUNCTION NOTGIVEN RETURN VARCHAR2 AS
IAD VARCHAR2(20);
IND VARCHAR2(20);
CURSOR C1 IS SELECT Q_ID FROM QUESTION WHERE NOT Q_ID in (SELECT Q.Q_ID FROM QUESTION Q INNER JOIN ANSWER A ON Q.Q_ID = A.Q_ID);
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO IND;
EXIT WHEN C1%NOTFOUND;
IF C1%FOUND THEN --if found then
SELECT Q_ID INTO IAD FROM QUESTION WHERE Q_ID = IND;
DBMS_OUTPUT.PUT_LINE(IAD);
END IF;
IF C1%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('no found');--if not found
END IF;
END LOOP;
CLOSE C1;
END NOTGIVEN;
i tried to make function which gives all that question whose answer is not there, it gives appropriate output but show this type of error ORA-06503: PL/SQL: Function returned without value , line 21 ORA-06512: at line at 5 i also tried exception but it not work.
You've written a function which is supposed to return a VARCHAR2 value, but there are no return statements in the code, and thus the routine doesn't return a value.
CREATE OR REPLACE FUNCTION NOTGIVEN
RETURN VARCHAR2
AS
IAD VARCHAR2(20);
IND VARCHAR2(20);
CURSOR C1 IS SELECT Q_ID
FROM QUESTION
WHERE NOT Q_ID IN (SELECT Q.Q_ID
FROM QUESTION Q
INNER JOIN ANSWER A
ON Q.Q_ID = A.Q_ID);
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO IND;
EXIT WHEN C1%NOTFOUND;
IF C1%FOUND THEN --if found then
SELECT Q_ID
INTO IAD
FROM QUESTION
WHERE Q_ID = IND;
DBMS_OUTPUT.PUT_LINE(IAD);
END IF;
IF C1%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('no found');--if not found
END IF;
END LOOP;
CLOSE C1;
RETURN IAD; -- added
END NOTGIVEN;
Here I've added a statement to return IAD just before the END NOTGIVEN.
Best of luck.
I have a following stored procedure in which I have used a cursor. Depending on whether the cursor return any records or not I need to do some processing.
But I am not sure how to check if the cursor return any records.
CREATE OR REPLACE PROCEDURE SP_EMPLOYEE_LOOKUP_BY_EMP_ID
(
IN_USER_ID IN NUMBER,
IN_EMPLOYEE_ID NUMBER,
IN_HC_AS_ON_DATE VARCHAR2,
emp_cursor OUT SYS_REFCURSOR
)
IS
CURSOR employees IS
SELECT * FROM EMPLOYEE e;
BEGIN
if(record exist ) then
FOR employee IN employees
LOOP
// do something
END LOOP;
else if employees is empty then
// do something else
END;
It's not possible to check if the cursor returns records without opening it.
(see here)
So you can either have some fast query just to see if there are records (using count for example),
Or, you can do it like this:
CREATE OR REPLACE PROCEDURE SP_EMPLOYEE_LOOKUP_BY_EMP_ID
(
IN_USER_ID IN NUMBER,
IN_EMPLOYEE_ID NUMBER,
IN_HC_AS_ON_DATE VARCHAR2,
emp_cursor OUT SYS_REFCURSOR
)
IS
is_found_rec boolean := false;
CURSOR employees IS
SELECT * FROM EMPLOYEE e;
BEGIN
FOR employee IN employees
LOOP
is_found_rec := true;
// do something
END LOOP;
if not is_found_rec then
// do something else
end if;
END;
I think that it possible only with FETCH. Try to use
if myCursor%found then
// some body
end if;
But if somebody know another way so correct me.
I like this way:
DECLARE
CURSOR my_cur
IS
SELECT 'a' AS a FROM DUAL WHERE 1=1;
my_rec my_cur%rowtype;
BEGIN
OPEN my_cur;
LOOP
FETCH my_cur INTO my_rec;
IF my_cur%NOTFOUND
THEN
IF my_cur%ROWCOUNT=0
THEN
-- do stuff when cursor empty
DBMS_OUTPUT.PUT_LINE('NOTFOUND,RC=0' || '_' || my_cur%ROWCOUNT || '_' || my_rec.a);
END IF;
EXIT;
ELSE
-- do stuff when cursor not empty
DBMS_OUTPUT.PUT_LINE('FOUND,RC>0' || '_' || my_cur%ROWCOUNT || '_' || my_rec.a);
END IF;
END LOOP;
CLOSE MY_CUR;
END;
Output when cursor is 1=1 (not empty):
FOUND,RC>0_1_a
Output when cursor is 1=0 (empty):
NOTFOUND,RC=0_0_
I like to use the same select query that comprises the cursor and select the count it returns into a variable. You can then evaluate the variable to determine what to do next. For example you have a cursor like this:
CURSOR c_example IS
SELECT field1
,field2
,field3
FROM table1 a
WHERE a.field1 LIKE '%something%';
Before you open the cursor to do any processing, select cursor result count into a variable.
SELECT count(*)
INTO v_exampleCount
FROM table1 a
WHERE a.field1 LIKE '%something%';
Now, again before you open cursor, do something like:
IF exampleCount>0 THEN
FOR example IN c_example
LOOP
....
END LOOP; --end example loop
....
END IF; --end example if
341/5000
One solution would be to work with reverse logic. It is e.g. not possible to ask:
IF my_record IS NULL THEN
do something;
END IF;
or
IF my_record.my_attibute IS NULL THEN
do something;
END IF;
Instead it is possible to ask:
IF my_record.my_attibute IS NOT NULL THEN
go on processing;
ELSE
do something;
END IF;
I'm just wondering why this piece of code is not working. I don't have any supplier id=1 in my table.
DECLARE
VAR SUPP_NM VARCHAR(100);
VAR_SUPP_ID NUMBER := 1;
WHILE_VAR CHAR := 'Y';
BEGIN
SELECT SUPP_NM
INTO VAR_SUPP_NM
FROM TEST.SUPPLIER
WHERE SUPP_ID = VAR_SUPP_ID;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('SQL DATA NOT FOUND');
ELSIF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('DATA FOUND');
END IF;
END;
I get a 01403 error in Toad but not handled as sql%notfound.
Why isn't the sql%notfound working?
To catch the NO_DATA_FOUND exception rewrite your code as follows by adding exception section:
DECLARE
VAR_SUPP_NM VARCHAR2(100);
VAR_SUPP_ID NUMBER := 1;
WHILE_VAR CHAR := 'Y';
BEGIN
SELECT SUPP_NM
INTO VAR_SUPP_NM
FROM TEST.SUPPLIER
WHERE SUPP_ID = VAR_SUPP_ID;
DBMS_OUTPUT.PUT_LINE('DATA FOUND');
exception
when no_data_found
then DBMS_OUTPUT.PUT_LINE('SQL DATA NOT FOUND');
END;
Checking SQL%FOUND or SQL%NOTFOUND have no meaning in the case of select into statement, because if the select statement returns no rows it will always raise no_data_found exception, except, if that select statement invokes aggregate function, it will always return data or null if no rows has been selected.
Do not use varchar datatype, use varchar2 datatype instead.
Nicholas's answer is what you want if you want to use SELECT INTO. However, if it is more important that you are able to use %FOUND or %NOTFOUND, consider FETCHing from a cursor instead:
DECLARE
VAR SUPP_NM VARCHAR2(100);
VAR_SUPP_ID NUMBER := 1;
WHILE_VAR CHAR := 'Y';
CURSOR c1 IS
SELECT SUPP_NM
FROM TEST.SUPPLIER
WHERE SUPP_ID = VAR_SUPP_ID;
BEGIN
OPEN c1;
FETCH c1 INTO VAR_SUPP_NM;
IF c1%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('SQL DATA NOT FOUND');
ELSIF c1%FOUND THEN
DBMS_OUTPUT.PUT_LINE('DATA FOUND');
END IF;
CLOSE c1;
END;
Nick's answer is correct.
In oracle documentation however it is stated that SQL%NOTFOUND works with SELECT INTO but before one could check SQL%NOTFOUND to be TRUE an error is generated called as no_data_found.
so to use SQL%NOTFOUND one first needs to hande no_data_found error.
DECLARE
VAR SUPP_NM VARCHAR(100);
VAR_SUPP_ID NUMBER := 1;
WHILE_VAR CHAR := 'Y';
BEGIN
BEGIN
SELECT SUPP_NM
INTO VAR_SUPP_NM
FROM TEST.SUPPLIER
WHERE SUPP_ID = VAR_SUPP_ID;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null; -- or write something here if u want.
END;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('SQL DATA NOT FOUND');
ELSIF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('DATA FOUND');
END IF;
END;
So what I have done is added a inner BEGIN-END block enclosing the SELECT statement that generates no_data_found exception. After that you can check for the value of SQL%NOTFOUND.
You can read more about this in oracle docs.
Start from this active link in mytime : https://docs.oracle.com/cd/B28359_01/appdev.111/b28370/errors.htm#LNPLS00703
I have a following stored procedure in which I have used a cursor. Depending on whether the cursor return any records or not I need to do some processing.
But I am not sure how to check if the cursor return any records.
CREATE OR REPLACE PROCEDURE SP_EMPLOYEE_LOOKUP_BY_EMP_ID
(
IN_USER_ID IN NUMBER,
IN_EMPLOYEE_ID NUMBER,
IN_HC_AS_ON_DATE VARCHAR2,
emp_cursor OUT SYS_REFCURSOR
)
IS
CURSOR employees IS
SELECT * FROM EMPLOYEE e;
BEGIN
if(record exist ) then
FOR employee IN employees
LOOP
// do something
END LOOP;
else if employees is empty then
// do something else
END;
It's not possible to check if the cursor returns records without opening it.
(see here)
So you can either have some fast query just to see if there are records (using count for example),
Or, you can do it like this:
CREATE OR REPLACE PROCEDURE SP_EMPLOYEE_LOOKUP_BY_EMP_ID
(
IN_USER_ID IN NUMBER,
IN_EMPLOYEE_ID NUMBER,
IN_HC_AS_ON_DATE VARCHAR2,
emp_cursor OUT SYS_REFCURSOR
)
IS
is_found_rec boolean := false;
CURSOR employees IS
SELECT * FROM EMPLOYEE e;
BEGIN
FOR employee IN employees
LOOP
is_found_rec := true;
// do something
END LOOP;
if not is_found_rec then
// do something else
end if;
END;
I think that it possible only with FETCH. Try to use
if myCursor%found then
// some body
end if;
But if somebody know another way so correct me.
I like this way:
DECLARE
CURSOR my_cur
IS
SELECT 'a' AS a FROM DUAL WHERE 1=1;
my_rec my_cur%rowtype;
BEGIN
OPEN my_cur;
LOOP
FETCH my_cur INTO my_rec;
IF my_cur%NOTFOUND
THEN
IF my_cur%ROWCOUNT=0
THEN
-- do stuff when cursor empty
DBMS_OUTPUT.PUT_LINE('NOTFOUND,RC=0' || '_' || my_cur%ROWCOUNT || '_' || my_rec.a);
END IF;
EXIT;
ELSE
-- do stuff when cursor not empty
DBMS_OUTPUT.PUT_LINE('FOUND,RC>0' || '_' || my_cur%ROWCOUNT || '_' || my_rec.a);
END IF;
END LOOP;
CLOSE MY_CUR;
END;
Output when cursor is 1=1 (not empty):
FOUND,RC>0_1_a
Output when cursor is 1=0 (empty):
NOTFOUND,RC=0_0_
I like to use the same select query that comprises the cursor and select the count it returns into a variable. You can then evaluate the variable to determine what to do next. For example you have a cursor like this:
CURSOR c_example IS
SELECT field1
,field2
,field3
FROM table1 a
WHERE a.field1 LIKE '%something%';
Before you open the cursor to do any processing, select cursor result count into a variable.
SELECT count(*)
INTO v_exampleCount
FROM table1 a
WHERE a.field1 LIKE '%something%';
Now, again before you open cursor, do something like:
IF exampleCount>0 THEN
FOR example IN c_example
LOOP
....
END LOOP; --end example loop
....
END IF; --end example if
341/5000
One solution would be to work with reverse logic. It is e.g. not possible to ask:
IF my_record IS NULL THEN
do something;
END IF;
or
IF my_record.my_attibute IS NULL THEN
do something;
END IF;
Instead it is possible to ask:
IF my_record.my_attibute IS NOT NULL THEN
go on processing;
ELSE
do something;
END IF;
I need to take cursor fields names from another cursor like this:
FOR rec1 IN (SELECT * FROM table1) LOOP
FOR rec2 IN (SELECT * FROM table2) LOOP
IF rec1.[rec2.field_name] <> '*' THEN
...
END IF;
END LOOP;
END LOOP;
Oracle is really not designed for this kind of behavior. The only way I can think of to achieve this is to use dynamic PL/SQL to produce the functionality you're looking for:
declare
v_field_value varchar2(2000);
begin
FOR rec1 IN (SELECT * FROM table1) LOOP
FOR rec2 IN (SELECT * FROM table2) LOOP
EXECUTE IMMEDIATE 'begin :value := :rec1.'
|| :rec2.field_name || '; end;'
USING OUT v_field_value, IN rec1;
IF v_field_value <> '*' THEN
...
END IF;
END LOOP;
END LOOP;
end;
However, just because this approach can work doesn't mean you should use it. If your field is not a string, for instance, Oracle will implicitly convert the value, which may result in a different value than what you expect. If this were my code, I would only use this as a last resort, after considering implementing the same functionality outside the database and redesigning the database's structure to avoid the need for this type of code.
Based on the comment the error mentioned in the comment, I've modified the code so to pass the records in using bind variables.
I am not sure, I understand what your problem is, you can access the outer cursor-loop's record just like you would expect from a variable that is declared in a scope above the current one.
for rec1 in (SELECT * FROM table1) loop
for rec2 in (SELECT * FROM table2) loop
if rec1.field = 1 and rec2.field_name <> '*' then
...
end if;
end loop;
end loop;
kinda like
declare
i Integer;
begin
declare
x Integer;
begin
i := x;
end;
end;