Passing data when query returns value and No "EXCEPTION WHEN NO_DATA_FOUND THEN" (Oracle 11g) - oracle

I have created a procedure for updating my t_ritm table. First I have select rrcd_qnty (which is my product quantity) of a product id from t_rrcd table. Then I update the rrcd_qnty value in t_ritm table.
Here is my procedure:
procedure update_ritm_new_rate(p_oid in varchar2, p_ritm_rate in varchar2, p_euser in varchar2)
is
nrate varchar2(4);
begin
SELECT rrcd_rate into nrate
FROM (select oid, t_rrcd.rrcd_rate
from t_rrcd
where rrcd_ritm= p_oid
ORDER BY oid DESC )
WHERE rownum <= 1
ORDER BY rownum DESC ;
EXCEPTION
WHEN NO_DATA_FOUND THEN nrate := 0;
update t_ritm
set ritm_rate = nrate, euser = p_euser, edat = sysdate
where oid = p_oid;
commit;
end update_ritm_new_rate;
Some of my product id Quantity was null. so I was getting No_Data_Found error. But when and which product id has Quantity value they were successfully updating. For avoiding No_Data_Found I used EXCEPTION WHEN NO_DATA_FOUND THEN nrate := 0; which solved my no_Data_Found error. But when product id has quantity value they were not updating.
I had search lot of for this issue but not get good solution. What should be the best practice for avoiding No_Data_Found error? Could I pass my value if I don't get any No_Data_Found error?
thank in advance

That's because - if your SELECT returns something, it never reaches UPDATE as it is hidden behind the EXCEPTION handler.
Therefore, enclose it (SELECT) into its own BEGIN-END block, and put UPDATE out of it so that it is executed with whichever NRATE value is used.
PROCEDURE update_ritm_new_rate (p_oid IN VARCHAR2,
p_ritm_rate IN VARCHAR2,
p_euser IN VARCHAR2)
IS
nrate VARCHAR2 (4);
BEGIN
BEGIN --> this
SELECT rrcd_rate
INTO nrate
FROM ( SELECT oid, t_rrcd.rrcd_rate
FROM t_rrcd
WHERE rrcd_ritm = p_oid
ORDER BY oid DESC)
WHERE ROWNUM <= 1
ORDER BY ROWNUM DESC;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
nrate := 0;
END; --> this
UPDATE t_ritm
SET ritm_rate = nrate, euser = p_euser, edat = SYSDATE
WHERE oid = p_oid;
COMMIT;
END update_ritm_new_rate;

I have fixed the issue by adding EXCEPTION WHEN NO_DATA_FOUND THEN nrate := 0; after the update query.
procedure update_ritm_new_rate(p_oid in varchar2, p_ritm_rate in varchar2, p_euser in varchar2)
is
nrate varchar2(4);
begin
SELECT rrcd_rate into nrate FROM (select oid, t_rrcd.rrcd_rate from t_rrcd where rrcd_ritm= p_oid ORDER BY oid DESC )
WHERE rownum <= 1 ORDER BY rownum DESC ;
update t_ritm set ritm_rate = nrate, euser = p_euser, edat = sysdate where oid = p_oid;
commit;
EXCEPTION WHEN NO_DATA_FOUND THEN nrate := 0;
end update_ritm_new_rate;

Related

Oracle Form Personalization Calling Custom Procedure

I am doing form personalization in HR module .my requirement is when the end-user is trying to terminate than a procedure should get executed and check the condition if the is not met then it should display an error in the front end form
I am using this procedure:=
CREATE OR REPLACE PROCEDURE validate_terminate IS
u_id NUMBER := fnd_global.user_id;
e_id NUMBER;
p_id NUMBER;
l_yes VARCHAR2 (1);
null_found EXCEPTION;
BEGIN
SELECT EMPLOYEE_ID
INTO e_id
FROM fnd_user
WHERE user_id = u_id;
SELECT person_id
INTO p_id
FROM per_all_people_f
WHERE person_id = e_id;
SELECT 'Y'
INTO l_yes
FROM DUAL
WHERE EXISTS
(SELECT 'Y'
FROM pa_expenditure_items_all paei,
pa_expenditures_all pae
WHERE paei.expenditure_id = pae.expenditure_id
AND pae.incurred_by_person_id = p_id
AND paei.cost_distributed_flag = 'N');
IF l_yes = NULL THEN
dbms_output.put_line ('USER CANNOT PERFORM TERMINATE');
RAISE null_found;
END IF;
EXCEPTION
WHEN null_found THEN
dbms_output.put_line ('CONDITION IS NOT MET');
dbms_output.put_line ('CANNOT PROCESS TERMINATE');
WHEN no_data_found THEN
dbms_output.put_line ('CONDITION IS NOT MET');
dbms_output.put_line ('CANNOT PROCESS TERMINATE');
END;
The procedure is working fine but I am unable to execute it in the front end
as I am doing these below are the screenshots
[front end from][1]
[ACTION tab image][2]
but the procedure is not getting executed.
dbms_output.put_line is only in pl/sql visible if serveroutput is on.
You won't see these messages in oracle forms.
You should use message or an alert followed by
raise form_trigger_failure

Value of variable in Oracle with Select Into not resolving during procedure

I have a timestamp column in one of my tables. I want to get all entries that are a minute after the very first entry in the table. The timestamp is chronological with respect to the primary id.
To this end I retrieve the first entry in the table and put the value of the time column into a variable. Then I add a minute to this value and create a new variable with the new value. Using this new variable I search for the entry that is less than the adjusted timestamp and retrieve the id of that entry.
So my three variables are v_time, v_adjusted and v_id.
When setting v_time I have the following query,
begin
select time_of
into v_time
from MYTABLE where rownum=1
order by id asc;
exception
when no_data_found then
v_time := null;
end;
For v_adjusted i simply do,
v_adjusted := v_time + 1 / 1440.00;
When setting v_id I have,
begin
select id
into v_id
from MYTABLE where time_of < v_adjusted and rownum=1
order by id desc;
exception
when no_data_found then
v_id:= null;
end;
These operations do not return the correct result. The retrieved id is wrong. I can confirm by executing a query with the new timestamp which returns the correct id. As an example, in my table adding a minute should return id 19 but the value of v_id is 1.
Why am I getting the wrong result?
You need to use subquery:
begin
SELECT id
INTO v_id
FROM (select id
from MYTABLE where time_of < v_adjusted
order by id desc
) sub
where rownum=1;
exception
when no_data_found then
v_id:= null;
end;
or simply max:
begin
select MAX(id)
into v_id
from MYTABLE where time_of < v_adjusted;
exception
when no_data_found then
v_id:= null;
end;
EDIT:
With FETCH (Oracle 12c):
begin
select id
into v_id
from MYTABLE where time_of < v_adjusted;
ORDER BY id DESC
FETCH FIRST 1 ROWS ONLY;
exception
when no_data_found then
v_id:= null;
end;

Compile error on if else condition in trigger

create or replace
TRIGGER TRG_DEPT_ID BEFORE INSERT ON DEPT FOR EACH ROW
BEGIN
IF NOT EXISTS (SELECT * FROM DEPT cd WHERE cd.st_num = :new.ST_NUMBER AND cd.td_NUMBER = :new.TD_NUMBER)
THEN
SELECT SEQ_DEPT_ID.NEXTVAL INTO :new.ID FROM dual;
ELSE
SELECT ID FROM DEPT INTO :new.ID WHERE cd.st_num = :new.ST_NUMBER AND cd.td_NUMBER = :new.TD_NUMBER;
END IF;
END ;
I am trying to check for duplicate entry. If not exists, then I will create a new Id from sequence. Else I will put the same id.
And the SaveOrUpdate(dept); will do the remaining.
But it gave me compile error.Error(8,9): PL/SQL: SQL Statement ignored.
UPDATE:
When I tried the following query:
CREATE OR REPLACE TRIGGER TRG_DEPT_ID
BEFORE INSERT ON DEPT FOR EACH ROW
BEGIN
BEGIN
SELECT ID
INTO :NEW.ID
FROM DEPT
WHERE cd.st_num = :NEW.ST_NUMBER
AND cd.td_NUMBER = :NEW.TD_NUMBER;
EXCEPTION WHEN no_data_found THEN
SELECT SEQ_DEPT_ID.NEXTVAL INTO :new.ID FROM dual;
END;
END;
Getting the error:
ORA-01422: exact fetch returns more than requested number of rows
How to handle this error?
You can simply code your trigger like this,
CREATE OR REPLACE TRIGGER TRG_DEPT_ID
BEFORE INSERT ON DEPT FOR EACH ROW
BEGIN
BEGIN
SELECT ID
INTO :NEW.ID
FROM DEPT
WHERE cd.st_num = :NEW.ST_NUMBER
AND cd.td_NUMBER = :NEW.TD_NUMBER
AND rownum = 1; --Use rownum = 1 to avoid selecting too many rows.
EXCEPTION WHEN no_data_found THEN
SELECT SEQ_DEPT_ID.NEXTVAL INTO :new.ID FROM dual;
--:NEW.id = seq_dept_id.nextval; -- Or you can use this if you are using 11g or higher versions.
END;
END;
Edited:
You can modify your trigger code as below if duplicate entries are there in your table.
CREATE OR REPLACE TRIGGER TRG_DEPT_ID
BEFORE INSERT ON DEPT FOR EACH ROW
DECLARE
l_id DEPT.id%type;
BEGIN
BEGIN
SELECT ID
INTO l_id
FROM DEPT
WHERE cd.st_num = :NEW.ST_NUMBER
AND cd.td_NUMBER = :NEW.TD_NUMBER
AND ROWNUM = 1; --Use rownum = 1 to avoid selecting too many rows.
IF l_id IS NOT NULL THEN --If same st_number and td_number exists.
raise_application_error( -20001, 'Duplicate entry.');
END IF;
EXCEPTION WHEN no_data_found THEN
SELECT SEQ_DEPT_ID.NEXTVAL INTO :new.ID FROM dual;
--:NEW.id = seq_dept_id.nextval; -- Or you can use this if you are using 11g or higher versions.
END;
END;
try adding rownum in the where section :
CREATE OR REPLACE TRIGGER TRG_DEPT_ID
BEFORE INSERT ON DEPT FOR EACH ROW
BEGIN
BEGIN
SELECT ID
INTO :NEW.ID
FROM DEPT
WHERE cd.st_num = :NEW.ST_NUMBER
AND cd.td_NUMBER = :NEW.TD_NUMBER
AND ROWNUM =1;
EXCEPTION WHEN no_data_found THEN
SELECT SEQ_DEPT_ID.NEXTVAL INTO :new.ID FROM dual;
END;
END;
That will make the query to just return 1 row in case cd.st_num and cd.td_NUMBER are repeated in more than one row.
On the other hand, if you need to detect that there are more than one row for these two columns , you can catch the exception TOO_MANY_ROWS in the exceptions section
EXCEPTION WHEN no_data_found THEN
SELECT SEQ_DEPT_ID.NEXTVAL INTO :new.ID FROM dual;
when TOO_MANY_ROWS then
-- your logic to do when there are too many rows
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.

How to find out if this SELECT query executed successfully?

CREATE PROCEDURE Pname(in_empno IN NUMBER out_name OUT VARCHAR2)
AS
BEGIN
select EmpName into out_name from emptable where Empno = in_empno;
END Pname;
In the above procedure, how can I check if the SELECT query executed successfully or not with the given condition?
You can use the EXCEPTION block to determine if rows were returned or if some other exceptions occurred. Try this
BEGIN
SELECT EmpName into out_name from emptable where Empno = in_empno
EXCEPTION
WHEN NO_DATA_FOUND THEN
out_name := NULL;
END;
If you would like to see the output of out_name parameter try as follows
CREATE OR REPLACE PROCEDURE pname (in_empno IN NUMBER, out_name OUT VARCHAR2)
IS
BEGIN
SELECT empName
INTO out_name
FROM emptable
WHERE empno = in_empno;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
raise_application_error (
-20000,
'Unable to execute procedure because of: ' || SQLERRM
);
END pname;
This procedure can be executed from sql plus as
var ret varchar2(512);
exec pname(2345,:ret);
print ret;
Update 1
If you would like to return a value if sql query executes successfully and if there is an error return another value then do as
CREATE OR REPLACE PROCEDURE pname (in_empno IN VARCHAR2,
out_name OUT VARCHAR2,
returnval IN OUT NUMBER
)
IS
BEGIN
SELECT employee_name
INTO out_name
FROM employees
WHERE emp_number = in_empno;
returnval := 0;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
returnval := 1;
END pname;
and call the procedure from sql plus as
var ret varchar2(512);
var ret2 number;
exec pname(2346,:ret,:ret2);
print ret;
print ret2;
In this example i can stop using one more out variable for return right? and ill check for the condition in my front end like if 'out_name' returns null ill handle in someway, if it returns value it'll be handled in other way, Which one is efficient?????Using one more out variable for Retvalue, or doing like this
CREATE OR REPLACE PROCEDURE pname (in_empno IN VARCHAR2, out_name OUT VARCHAR2)
IS
BEGIN
SELECT employee_name
INTO out_name
FROM employees
WHERE emp_number = in_empno;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
out_name := NULL;
END;

Resources