pl sql %NOTFOUND - oracle

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

Related

Oracle PL/SQL dealing with values not found in the cursor

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;

'PLS-00049: bad bind variable' when compiling function

I am getting error while I compile this function:
PLS-00049: bad bind variable 'RETURN_VALUE'
I need help with this please.
FUNCTION GECM_ROUND_FNC
(P_FIELD_VALUE IN NUMBER,
P_ORG_ID IN NUMBER)
RETURN VARCHAR2
IS
v_ret_val NUMBER := P_FIELD_VALUE;
V_OU_NAME hr_operating_units.name%type;
v_round_dec NUMBER;
BEGIN
BEGIN
SELECT NAME INTO V_OU_NAME
FROM HR_ALL_ORGANIZATION_UNITS
WHERE ORGANIZATION_ID = P_ORG_ID;
EXCEPTION WHEN OTHERS THEN
V_OU_NAME := '';
END;
--
IF V_OU_NAME IS NOT NULL AND NVL(GECM_ICP_PKG.gecm_get_process_value_fnc('GECM_ERP_VALIDATIONS',V_OU_NAME,'','',''),'N')='Y' THEN
BEGIN
IF NVL(GECM_ICP_PKG.GECM_GET_PARAMETER_VALUE_FNC('GECM_ERP_VALIDATIONS','DECIMAL_ROUNDING_INTERFACES',V_OU_NAME,'',''),0) = 'Y' THEN
BEGIN
SELECT NVL(GECM_ICP_PKG.GECM_GET_PARAMETER_VALUE_FNC('GECM_ERP_VALIDATIONS','DECIMAL_PLACE_LIMIT',V_OU_NAME,'',''),0)
INTO v_round_dec from dual;
EXCEPTION
WHEN OTHERS THEN
v_round_dec := 0;
END;
IF v_round_dec <> 0 THEN
v_ret_val:= TO_CHAR(ROUND(v_ret_val,v_round_dec));
ELSE
v_ret_val:=v_ret_val;
END IF;
END IF;
END;
END IF;
SELECT to_char(decode(sign(v_ret_val-1),-1,decode('0'||to_number(v_ret_val),'00','0','0'||to_number(v_ret_val)),v_ret_val)) INTO v_ret_val FROM DUAL;
:return_value := v_ret_val;
END;
Instead of
:return_value := v_ret_val;
use
return v_ret_val;

Bypass no data execution Oracle PL/SQL

I am trying to check for two kinds of authorization level for entries in the menu to display according to different levels given to each user.
But the no data exception in oracle sql is preventing me from checking the second table for authorization.
Here is my code:
declare
v_count number;
v_name varchar2(255);
begin
select DASHBOARD into v_count from RCM_ADMINISTRATOR where USER_NAME = :APP_USER;
select MANAGER into v_name from RCM_ADMINISTRATION_TEAMS where MANAGER = :APP_USER;
if v_count >= 1 then
return true;
end if;
if v_name is not null then
return true;
end if;
if v_count = 0 then
return false;
end if;
EXCEPTION WHEN NO_DATA_FOUND THEN
return false;
end;
Is there a way to make it so that if I get no data from the first query, I can still run the second query?
This is a very common problem, with many solutions.
One option is to put a BEGIN / EXCEPTION / END block around each SELECT INTO that you're trying to do.
declare
v_count number;
v_name varchar2(255);
begin
BEGIN
select DASHBOARD into v_count from RCM_ADMINISTRATOR where USER_NAME = :APP_USER;
EXCEPTION when no_data_found THEN v_count := null;
END;
BEGIN
select MANAGER into v_name from RCM_ADMINISTRATION_TEAMS where MANAGER = :APP_USER;
EXCEPTION when no_data_found THEN v_name := null;
END;
if v_count >= 1 then
...etc...
I often find it easier just to use MAX:
declare
v_count number;
v_name varchar2(255);
begin
select max(DASHBOARD) into v_count from RCM_ADMINISTRATOR where USER_NAME = :APP_USER;
select max(MANAGER) into v_name from RCM_ADMINISTRATION_TEAMS where MANAGER = :APP_USER;
if v_count >= 1 then
...etc...
It's a lazy technique, and if you aren't careful, it can disguise ORA-01422 errors (exact fetch returns more than requested number of rows). But if you know your query will only return 0 or 1 row, then it will work fine.
There are several issues with your code. You are using an anonymous block and returning a value, which is not possible. You can write a function which returns a BOOLEAN value(True or False) However, it is of no use because as per Oracle documentation
http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/datatypes.htm#CJACJGBG.
You cannot insert the values TRUE and FALSE into a database column.
You cannot select or fetch column values into a BOOLEAN variable.
Functions called from a SQL query cannot take any BOOLEAN parameters.
Neither can built-in SQL functions such as TO_CHAR; to represent
BOOLEAN values in output, you must use IF-THEN or CASE constructs to
translate BOOLEAN values into some other type, such as 0 or 1, 'Y' or
'N', 'true' or 'false', and so on.
So In the code, VARCHAR2 is used as RETURN argument.
Pass APP_USER as function argument instead of using a bind variable.
For your condition that each query must execute irrespective of exceptions, you can place it in separate BEGIN..END blocks.
Once you have used RETURN from a stored procedure, you cannot jump back to the rest of the code after exception as you are trying to achieve. Instead, store the value in a variable exit_code and RETURN using the code before the last END to return eventually with an exit code if other conditions are not satisfied.
CREATE OR REPLACE FUNCTION f_test_ret(p_app_user IN VARCHAR2)
RETURN VARCHAR2
AS
v_count NUMBER;
v_name VARCHAR2(255);
exit_code VARCHAR2(6);
BEGIN
BEGIN
SELECT DASHBOARD
INTO v_count
FROM RCM_ADMINISTRATOR
WHERE USER_NAME = p_app_user;
EXCEPTION
WHEN NO_DATA_FOUND THEN
exit_code := 'FALSE';
END;
BEGIN
SELECT MANAGER
INTO v_name
FROM RCM_ADMINISTRATION_TEAMS
WHERE MANAGER = p_app_user;
EXCEPTION
WHEN NO_DATA_FOUND THEN
exit_code := 'FALSE';
END;
IF v_count >= 1 THEN
RETURN 'TRUE';
END IF;
IF v_name IS NOT NULL THEN
RETURN 'TRUE';
END IF;
IF v_count = 0 THEN
RETURN 'TRUE';
END IF;
RETURN exit_code;
END;
/

Oracle PL/SQL - how do I run the same block of code for ALL exceptions?

Oracle 11g. This seems like it should be stupidly obvious, but I haven't seen an example. I have 2 exceptions which each need to write slightly different log messages, and then they should do the same UPDATE and CONTINUE.
Is there any way to structure the exception so I only need to type the UPDATE and CONTINUE statements once, while keeping the different logging?
FOR my_rec IN my_cursor
LOOP
BEGIN
...do some stuff
EXCEPTION
WHEN NO_DATA_FOUND THEN
log_detail.new('Skipping record - ID not found');
UPDATE my_table
SET operation_result = 'Failed'
WHERE my_id = my_rec.some_id;
CONTINUE;
WHEN OTHERS THEN
log_detail.new('Skipping record - unknown error');
UPDATE my_table
SET operation_result = 'Failed'
WHERE my_id = my_rec.some_id;
CONTINUE;
END;
END LOOP;
Did you try:
FOR my_rec IN my_cursor
LOOP
BEGIN
...do some stuff
EXCEPTION
WHEN OTHERS THEN
if sqlcode=-1403 then
log_detail.new('Skipping record - ID not found');
else
log_detail.new('Skipping record - unknown error');
end if;
UPDATE my_table
SET operation_result = 'Failed'
WHERE my_id = my_rec.some_id;
CONTINUE;
END;
END LOOP;
You could try:
DECLARE
vError VARCHAR2(1);
vMessage VARCHAR2(100);
BEGIN
FOR my_rec IN my_cursor LOOP
vError := 'N';
BEGIN
...do some stuff
EXCEPTION
WHEN NO_DATA_FOUND THEN
vError := 'S';
vMessage := 'Skipping record - ID not found';
WHEN OTHERS THEN
vError := 'S';
vMessage := 'Skipping record - unknown error';
END;
IF vError = 'S' THEN
log_detail.new(vMessage);
UPDATE my_table
SET operation_result = 'Failed'
WHERE my_id = my_rec.some_id;
CONTINUE;
END IF;
END LOOP;
END;

Is it possible to use sql%rowcount for SELECT?

The code below may return more than one row. Will sql%rowcount return the number of rows fetched?
select * from emp where empname = 'Justin' and dept='IT'
if sql%rowcount>0
...
This is my sample proc; am I using sql%rowcount in correct way?
CREATE PROCEDURE Procn(in_Hid IN VARCHAR2,outInststatus OUT VARCHAR2,outSockid IN NUMBER,outport OUT VARCHAR2,outIP OUT VARCHAR2,outretvalue OUT NUMBER)
AS
BEGIN
select INST_STATUS into outInststatus from TINST_child where INST_ID = in_Hid and INST_STATUS = 'Y';
if outInststatus = 'Y' then
select PORT_NUMBER,STATIC_IP into outport,outIP from TINST where INST_ID = in_Hid and IP_PORT_STATUS = 'Y';
if sql%rowcount >= 1 then
select SOCK_ID into outSockid from TINST where PORT_NUMBER = outport AND STATIC_IP = outIP;
outretvalue := 0;
else
outretvalue := -12;
end if;
EXCEPTION
WHEN NO_DATA_FOUND THEN
outretvalue := -13;
end if;
END;
Yes, you can use SQL%ROWCOUNT. It's valid in PL/SQL.
However, in PL/SQL the result of your query needs to go somewhere e.g. into a PL/SQL table. PL/SQL will never send the result to the output (terminal, window etc.). So SELECT * FROM won't work.
Your code could look like this:
DECLARE
TYPE emp_t ...;
emp_tab emp_t;
BEGIN
SELECT *
BULK COLLECT INTO emp_tab
FROM emp
WHERE empname = 'Justin' AND dept='IT';
IF sql%rowcount > 0 THEN
.. do something ...
END IF;
END;
/
Update:
The updated questions suggests that you're looking for something else.
Option 1: Use exceptions
If there are 0 rows or more than 1 row, these cases are handled separately (as errors):
BEGIN
select PORT_NUMBER,STATIC_IP into outport, outIP
from TINST
where INST_ID = in_Hid AND IP_PORT_STATUS = 'Y';
EXCEPTION
WHEN NO_DATA_FOUND THEN
outretvalue := -12;
RETURN;
WHEN TOO_MANY_ROWS THEN
outretvalue := -13;
RETURN;
END;
Option 2: Use aggregations
Using aggregations, the query will always return exactly one row. If now source row matched the WHERE clause, then both result values will be NULL. If there WHERE clause matched more than one row, the maximum will be taken.
Note that this query might return a port number and an IP address that originally were not on the same row.
select MAX(PORT_NUMBER), MAX(STATIC_IP) into outport, outIP
from TINST
where INST_ID = in_Hid AND IP_PORT_STATUS = 'Y';
IF outport IS NULL OR outIP IS NULL THEN
outretvalue := -12;
RETURN;
END IF;
Option 3: Use ROWNUM
This query returns at most one row. If no row matched the WHERE clause, an exception is thrown and needs to be handled:
BEGIN
select PORT_NUMBER, STATIC_IP into outport, outIP
from TINST
where INST_ID = in_Hid AND IP_PORT_STATUS = 'Y'
AND ROWNUM = 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
outretvalue := -12;
RETURN;
END;
Based on your comment
If 2nd 'select' query returns more than one row i want to take the first one and process with it
... this ought to work, but perhaps not quite as you expect, as you haven't defined what the 'first one' means.
CREATE PROCEDURE Procn(in_Hid IN VARCHAR2, outInststatus OUT VARCHAR2,
outSockid IN NUMBER, outport OUT VARCHAR2, outIP OUT VARCHAR2,
outretvalue OUT NUMBER)
AS
BEGIN
select INST_STATUS into outInststatus
from TINST_child
where INST_ID = in_Hid and INST_STATUS = 'Y';
-- no need to check if outInstatus is Y, that's all it can be here
-- restricting with `rownum` means you'll get at most one row, so you will
-- not get too_many_rows. But it will be an arbitrary row - you have no
-- criteria to determine which of the multiple rows you want. And you can
-- still get no_data_found which will go to the same exception and set -12
select PORT_NUMBER, STATIC_IP into outport, outIP
from TINST
where INST_ID = in_Hid and IP_PORT_STATUS = 'Y'
and rownum < 2;
-- no need to check sql%rowcount; it can only be 1 here
-- not clear if this can return multiple rows too, and what should happen
-- if it can; could use rownum restriction but with the same caveats
select SOCK_ID into outSockid
from TINST
where PORT_NUMBER = outport AND STATIC_IP = outIP;
outretvalue := 0;
EXCEPTION
WHEN NO_DATA_FOUND THEN
outretvalue := -12;
END;
The exception handler applies to the whole block. If any of the select statements find no rows, the no_data_found exception will be handled by that block and will set outretvalue to -12.
If you want a different outretvalue for each select then you can wrap them in sub-blocks, each with their own exception handling section:
CREATE PROCEDURE Procn(in_Hid IN VARCHAR2, outInststatus OUT VARCHAR2,
outSockid IN NUMBER, outport OUT VARCHAR2, outIP OUT VARCHAR2,
outretvalue OUT NUMBER)
AS
BEGIN
BEGIN
select INST_STATUS into outInststatus
from TINST_child
where INST_ID = in_Hid and INST_STATUS = 'Y';
EXCEPTION
WHEN NO_DATA_FOUND THEN
outretvalue := -12;
END;
BEGIN
select PORT_NUMBER, STATIC_IP into outport, outIP
from TINST
where INST_ID = in_Hid and IP_PORT_STATUS = 'Y'
and rownum < 2;
EXCEPTION
WHEN NO_DATA_FOUND THEN
outretvalue := -13;
END;
BEGIN
select SOCK_ID into outSockid
from TINST
where PORT_NUMBER = outport AND STATIC_IP = outIP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
outretvalue := -14;
END;
outretvalue := 0;
END;
You only need to do that if the caller needs to know which select failed, and if you never really expect any of them to fail then it's probably more common not to catch the exception at all and let the caller see the raw no_data_found and decide what to do. Depends what the exception condition means to you and your application though.

Resources