Bypass no data execution Oracle PL/SQL - oracle

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

Related

Function returns 1 even when there's not matching record

I have the following function on a package. When running that the function against non-matching record, the result is always 1.
This is how I run the function and see output in DBMS Output window
set serveroutpuut on;
begin
dbms_output.put_line (BP$BUSINESSPARNTNER_CODE.Check_rec('44887'));
end;
This is the function that's responsible for query and counting the records
FUNCTION Check_rec(PartnerNumber IN VARCHAR2) RETURN NUMBER IS v_count NUMBER;
BEGIN
select count(PartnerNumber) into v_count from BusinessPartner where PartnerNumber = PartnerNumber and code is not null;
RETURN v_count;
END;
I have tried running with the hard-coded value and it works
FUNCTION Check_rec(PartnerNumber IN VARCHAR2) RETURN NUMBER IS v_count NUMBER;
BEGIN
select count(PartnerNumber) into v_count from BusinessPartner where PartnerNumber = '44887' and code is not null;
RETURN v_count;
END;
Please try changing the name of your IN parameter so that it does not conflict with column names:
FUNCTION Check_rec(pn IN VARCHAR2) RETURN NUMBER IS v_count NUMBER;
BEGIN
SELECT COUNT(PartnerNumber) INTO v_count
FROM BusinessPartner
WHERE PartnerNumber = pn AND code IS NOT NULL;
RETURN v_count;
END;
I don't know the rules which Oracle would use to resolve your current situation, but even if it could be made to work, it would probably be best to just avoid it.

PL/SQL ORA-00905: missing keyword for Set Value

I searched for this error, but since it's very vague, I could not find something similar to understand where is the problem. This code is actually for an Oracle Apex application. I actually have bind variables instead of numbers 1 and 84 (which I confirm are correct values within my tables), but still got same error.
After declaring the variables, it selects a string that will be the name of a column within another table and put it V_COLUMN.
Then i dynamically build a query to get the value of this column and put it into V_VALUE and finally I return a value (which is then shown in a form textfield). Unfortunately it returns the ORA 00905.
When I tried to run the sql commands separately using known values, it runs. So I think there must be some syntax problem somewhere in the dynamic sql. Thanks for any assistance.
DECLARE
V_COLUMN VARCHAR2(50) := 'UNKNOWN';
V_VALUE VARCHAR2(50) := 0;
V_SQL VARCHAR2(500);
BEGIN
SELECT SUB_CAT_ABBREV INTO V_COLUMN FROM SUB_CATEGORY WHERE SUB_CATEGORY_ID = 1;
V_SQL := 'SELECT ' || V_COLUMN || ' INTO V_VALUE FROM PLANNED_EFFORTS WHERE PLAN_ID = 84';
EXECUTE IMMEDIATE V_SQL;
RETURN V_VALUE;
EXCEPTION
WHEN no_data_found THEN
RETURN 'No Data Found Error';
WHEN too_many_rows then
RETURN 'Too many rows';
WHEN OTHERS THEN
RETURN 'Other Error';
END;
Just get rid off your INTO clause from your dynamic SQL statement:
V_SQL := 'SELECT ' || V_COLUMN || ' FROM PLANNED_EFFORTS WHERE PLAN_ID = 84';
EXECUTE IMMEDIATE V_SQL
INTO V_VALUE
Moreover, if you expect more then one value you can use BULK COLLECT INTO and return values into some collection type:
V_SQL := 'SELECT ' || V_COLUMN || ' FROM PLANNED_EFFORTS WHERE PLAN_ID = 84;
EXECUTE IMMEDIATE V_SQL
BULK COLLECT INTO V_VALUES
where V_VALUES can be declared as:
TYPE TABLE_OF_VARCHAR2 IS TABLE OF VARCHAR2(50);
V_VALUES TABLE_OF_VARCHAR2;
and accessed in the loop as follows:
for i in V_VALUES.FIRST .. V_VALUES.LAST LOOP
-- V_VALUES(i)
END LOOP;

PL/SQL: PLS-00382: expression is of wrong type. Statement ignored

I have a 'person' table with two fields 'person_name' and 'person_age'.
i want write a procedure that return sys_refcursor but calculating a extra field 'is_old'. por example:
PROCEDURE people_load(p_name IN VARCHAR2, P_RESULT OUT SYS_REFCURSOR) IS
BEGIN
DECLARE
isOld BOOLEAN := false;
CURSOR cursorTemp IS SELECT person_name, person_age, is_old
WHERE person_name = p_name;
BEGIN
FOR _p IN cursorTemp
LOOP
IF _p.person_age > 75 THEN
_p.is_old:=TRUE;
END IF;
END LOOP;
¿¿P_RESULT:=cursorTemp; //open P_RESULT for (open cursorTemp);??
END;
END people_load;
i dont know how to assign temporal cursor 'cursorTemp' to OUT param 'P_RESULT' to returning the result.
You can not use a BOOLEAN in SQL, only in PL/SQL.
You can not loop through a cursor and recalculate a column.
You declare a variable isOld and never use it.
I suggest you calculate the is_old within the cursor. I changed it to contain 1 (true) or 0 (false).
PROCEDURE people_load(p_name IN VARCHAR2, P_RESULT OUT SYS_REFCURSOR) IS
BEGIN
DECLARE
CURSOR cursorTemp IS SELECT person_name, person_age, case when person_age > 75 then 1 else 0 end is_old
WHERE person_name = p_name;
BEGIN
P_RESULT := cursorTemp; //open P_RESULT for (open cursorTemp);
END;
END people_load;
You should do the calculation in the select.
CURSOR cursorTemp IS
SELECT
person_name,
person_age,
CASE WHEN person_age>75 THEN 1 ELSE 0 END AS is_old
WHERE person_name = p_name;

oracle stored procedure not working

the below procedure complies fine,
CREATE PROCEDURE PROCEDURE1
(v_MGR int,
v_empid IN OUT int)
AS
BEGIN
v_empid :=0;
IF (v_mgr IS NOT NULL AND v_mgr <> '') then
EXECUTE IMMEDIATE 'SELECT EMPNO
FROM EMP
WHERE MGR = Rtrim(v_MGR)' into v_empid;
END IF;
END PROCEDURE1;
but when i run
DECLARE
V_MGR NUMBER;
V_EMPID NUMBER;
BEGIN
V_MGR := 7902;
V_EMPID := NULL;
PROCEDURE1(
V_MGR => V_MGR,
V_EMPID => V_EMPID
);
DBMS_OUTPUT.PUT_LINE('V_EMPID = ' || V_EMPID);
END;
the output should be v_empid =2356
but its always showing v_empid = 0 please help to get proper answer
Why is the out parameter is 0? Take a look at the if condition in the procedure
IF (v_mgr IS NOT NULL AND v_mgr <> '')
especially at its second part AND v_mgr <> ''. Oracle treats empty string '' as null and any comparison to null leads to unknown result, thus the above IF condition always evaluates to false so execute immediate statement never executes and as a result the value of v_empid
will never be overwritten.
In this particular situation there is absolutely no need of using dynamic SQL(native dynamic sql execute immediate), because there is no dynamic construction of the query - table and columns are known at compile-time. You simply could use static sql instead:
If your query returns more than one row, you will hit too_many_rows exception. You either should guarantee that your query returns exactly one row, by including rownum=1 in the where clause of the query(if there is a change of returning multiple rows) or you use a collection as the out parameter, to return the result set:
create or replace type T_EmpNums is table of number;
/
create or replace procedure procedure1(
v_mgr int,
v_emps out T_empnums
)
as
begin
if v_mgr is not null
then
select empno
bulk collect into v_emps
from emp
where mgr = v_mgr;
end if;
end;
/
declare
v_mgr number;
v_empids T_EmpNums;
begin
v_mgr := 7902;
procedure1(v_mgr, v_empids);
if v_empids is not empty
then
for empno in v_empids.first .. v_empids.last
loop
dbms_output.put_line('v_empid = ' || to_char(v_empids(empno)));
end loop;
end if;
end;
Well, you declare v_MGR int and then you go and test this v_mgr <> '' and use this Rtrim(v_MGR) in your condition.
Which is it, (var)char or number for mgr and v_mgr?
try this
CREATE PROCEDURE PROCEDURE1
(v_MGR int,
v_empid IN OUT int)
AS
BEGIN
v_empid :=0;
IF (v_mgr IS NOT NULL AND v_mgr <> '') then
SELECT EMPNO into v_empid
FROM EMP
WHERE MGR = Rtrim(v_MGR) ;
END IF;
END PROCEDURE1;

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