I have this procedure which just deletes a row based on a column field called AppID. This procedure get's a value from another column called AppNbr based on that rows AppID column. The procedure is failing with a TOO_MANY_ROWS exception when it tries to SELECT a row. This is the PL/SQL:
DECLARE
lvnApplNbr NUMBER;
PROCEDURE deleteAppl(applId IN VARCHAR2) IS
BEGIN
BEGIN
SELECT ApplNbr -- Exception thrown here
INTO lvnApplNbr
FROM Appl
WHERE ApplID = applId;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
-- ... Delete it after some logic
END; -- End Procedure
BEGIN
...
deleteAppl('571E00BA-70E6-4523-BEAC-4568C3DD1A7D');
...
END;
The TOO_MANY_ROWS exception is thrown when it SELECT INTOs. I have no idea why it is throwing that error because if I just query this:
SELECT ApplNbr FROM Appl WHERE ApplId = '571E00BA-70E6-4523-BEAC-4568C3DD1A7D';
Only one row will come back with the correct ApplId.
What is going on?
Just use an alias for the related table (Appl):
PROCEDURE deleteAppl(applId IN VARCHAR2) IS
.....
.....
SELECT ApplNbr
INTO lvnApplNbr
FROM Appl a
WHERE a.ApplID = applId;
......
or change your parameter's name (applId) to another name such as i_applId :
PROCEDURE deleteAppl(i_applId IN VARCHAR2) IS
.....
.....
SELECT ApplNbr
INTO lvnApplNbr
FROM Appl
WHERE ApplID = i_applId;
......
Because in your case multiple matching perceived, if your parameter's name and column name are identical.
Related
I have a stored procedure. I would like to implement the below logic, which I have written in pseudocode.
If the below query has one of more entries:
SELECT
NULL
FROM
table1
WHERE
condition
GROUP BY
column
HAVING
COUNT(1) > 1
UNION ALL
SELECT
NULL
FROM
table1 a
WHERE
condition
AND EXISTS (
SELECT
NULL
FROM
table2 b
WHERE
condition
);
Then raise an exception and stop the stored procedure.
Here is an example of raising an exception if a particular value is found from a query:
declare
somevar dual.dummy%type;
begin
select 'Y' into somevar
from dual;
if somevar = 'Y' then
raise_application_error(-20123, 'Hull breach on deck 15. Abandon ship.');
end if;
end;
The "select from dual" can be any query, so feel free to substitute your unions and counts (though we should really stick to the standard count(*), not count('Dracula') etc).
Let's do this with the sample emp/dept schema - just plug in your own statement for your use case. You do need to declare since in pl/sql you cannot "just select". You always need to select into a variable. I usually just select the number 1 into a dummy variable of type number. The trick is to raise the exception after the SELECT INTO and do nothing on NO_DATA_FOUND.
You can use named exceptions to distinguish different cases but since a no data found will throw an exception you have to do each of the cases in its own block. The cleanest is to handle all named exceptions in the final exception block.
DECLARE
l_dummy NUMBER;
king_exists EXCEPTION;
dave_exists EXCEPTION;
BEGIN
BEGIN
SELECT 1 INTO l_dummy FROM emp WHERE ename = 'DAVE';
RAISE dave_exists;
EXCEPTION WHEN NO_DATA_FOUND THEN
NULL;
END;
BEGIN
SELECT 1 INTO l_dummy FROM emp WHERE ename = 'KING';
RAISE king_exists;
EXCEPTION WHEN NO_DATA_FOUND THEN
NULL;
END;
EXCEPTION
WHEN dave_exists THEN
raise_application_error(-20000,'My expection error message');
WHEN king_exists THEN
raise_application_error(-20001,'King exists');
END;
/
I am trying to determine why my exception output is not returning correctly.
This is for Oracle SQL Developer, practicing with PL/SQL. I can provide the table code if requested.
SET serveroutput on
CREATE OR REPLACE Procedure GetPermissions(user IN VARCHAR, docNum OUT
INTEGER)
IS
BEGIN
SELECT count(*) INTO docNum FROM UserPermissions where UserName=user;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Nothing found for this username');
END;
/
DECLARE
doc_count INTEGER;
BEGIN
doc_count:=0;
GetPermissions('Steve', doc_count);
dbms_output.put_line(' Number of documents: ' || doc_count);
END;
/
I am expecting the exception to return its output (nothing found) when 'Steve' is ran through as that is not a username in the table I created. When running currently is just shows "Number of documents: 0".
COUNT always returns a result, in this case, it returns a single row with 0 in it:
COUNT(*)
--------
0
NO_DATA_FOUND occurs only when the SELECT INTO query doesn't return any row.
For example, NO_DATA_FOUND will be thrown, if you try to select permission for a given user:
SELECT permission
INTO p_permission
FROM UserPermissions
WHERE UserName=user;
In this case, the query will return an empty result.
select into query returns with No_Data_found exception when the out put of the query is no rows. not when it is 0.
Another exception you get with select into clause is too many rows where the query returns more that 1 row and you are trying to assign all those to a variable.
Given a PL/SQL function which looks a bit like:
Function f(pVar IN VARCHAR2) return VARCHAR2 IS
vs_ret VARCHAR2 := NULL;
BEGIN
select name into vs_ret from people where nickname = pVar;
return vs_ret;
END f;
What happens if people.nickname has no uniqueness constraint? What happens if (when) two people have the same nickname - will it lead to an error or just return the value from first row the statement returns?
This appears to be existing functionality which I'm tweaking, so options are somewhat limited to change everything.
It will throw a predefined TOO_MANY_ROWS (ORA-01422) exception. You can handle the exception like this:
CREATE FUNCTION f(
pVar IN VARCHAR2
) RETURN VARCHAR2
IS
vs_ret VARCHAR2;
BEGIN
SELECT name
INTO vs_ret
FROM people
WHERE nickname = pVar;
RETURN vs_ret;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
RETURN NULL; -- or you could do: RETURN 'Error: Too Many Rows';
WHEN NO_DATA_FOUND THEN
RETURN NULL; -- or you could do: RETURN 'Error: Not Found';
END f;
Or, you can leave the exception unhandled; in which case, the exception will get passed back up the hierarchy of calling blocks and each will get a chance to handle it and if it remains unhandled will terminate the query with a ORA-01422: exact fetch returns more than requested number of rows.
An alternative, if you only want the first name returned regardless of how many matches there actually are, is to add AND ROWNUM = 1 to the WHERE clause of the SELECT query (that way there will never be more than one row returned - although there could still be zero rows returned).
Another alternative, if you really do want multiple values (or no values) returned, is to use BULK COLLECT INTO and a collection:
CREATE FUNCTION f(
pVar IN VARCHAR2
) RETURN SYS.ODCIVARCHAR2LIST
IS
vs_ret SYS.ODCIVARCHAR2LIST;
BEGIN
SELECT name
BULK COLLECT INTO vs_ret
FROM people
WHERE nickname = pVar;
RETURN vs_ret;
END f;
unsure if this is useful to you but in instances where I don't care about too many rows or no data found I change it to a cursor.
that way in the no data found it doesn't go into the loop, and for too many rows it just loops that many times.
/* Formatted on 12/17/2015 8:18:33 AM (QP5 v5.115.810.9015) */
FUNCTION f (pVar IN VARCHAR2)
RETURN VARCHAR2
IS
vs_ret VARCHAR2 := NULL;
BEGIN
FOR c IN (SELECT name
FROM people
WHERE nickname = pVar)
LOOP
vs_ret := c.name;
END LOOP;
RETURN vs_ret;
END f;
You will get an error, that you might want to catch if you know something meaningful to do when the error occurs.
drop table people;
create table people (name varchar2(200), nickname varchar2(200));
insert into people values('name1','nick1');
insert into people values('name1','nick1');
select * from people;
create or replace function f(pVar IN VARCHAR2)
return VARCHAR2
IS
vs_ret people.name%type;
BEGIN
select name into vs_ret from people where nickname = pVar;
return vs_ret;
END f;
select f('nick1') from dual;
--- ==> error
create or replace function f(pVar IN VARCHAR2)
return VARCHAR2
IS
vs_ret people.name%type;
BEGIN
select name into vs_ret from people where nickname = pVar;
return vs_ret;
exception
when TOO_MANY_ROWS then
return null; -- This is no good solution but just to demo it
END f;
select f('nick1') from dual;
-- ==> null as Output
Script Output
Table dropped.
Table created.
1 row created.
1 row created.
NAME
--------------------------------------------------------------------------------
NICKNAME
--------------------------------------------------------------------------------
name1
nick1
name1
nick1
2 rows selected.
Function created.
select f('nick1') from dual
*
Error at line 1
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "DB.F", line 6
Function created.
F('NICK1')
--------------------------------------------------------------------------------
1 row selected.
Create or replace procedure disp(pEMPLASTNAME varchar2)
IS
Row employee%rowtype;
begin
select * into row from employee where EMPLASTNAME=’pEMPLASTNAME’ ;
dbms_output.put_line('Name: '||Row.EMPID||' '|| Row.EMPNAME);
End;
/
BEGIN
disp(‘Mark’);
END;
/
Hello, I am trying to display data from a a table using stored procedures. The last name is passed as a parameter through the stored procedure and upon execution , the stored procedure should display all the rows which have the last name . Here is the error that i get; pls help! :-
SQL> BEGIN
disp('Mark');
END;
/
BEGIN
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at "TEST.DISP", line 5
ORA-06512: at line 2
No need of the quotes:
select * into row from employee where EMPLASTNAME=pEMPLASTNAME;
However, you may not have any data for that variable's value anyway (i.e. one day, it may happen)
That's why I recommend you catch the exception and treat it (see EXCEPTION block)
pd: it is a good practice to not use reserved words such as row. I recommend you name your variable another way.
I would suggest using a cursor to select all rows and then looping through the cursor to print the result:
Create or replace procedure disp(pEMPLASTNAME varchar2)
IS
Cursor row_select is
select EMPID, EMPNAME from employee where emplastname = pEMPLASTNAME;
-- and whatever columns you need to print, using * isn't good practice
begin
for item in row_select loop
dbms_output.put_line('Name: '||item.EMPID||' '|| item.EMPNAME);
end loop;
End;
/
BEGIN
disp(‘Mark’);
END;
/
I would like to check if cursor have only one row.
and return this,
--Please see my question inside the procedure?
CREATE OR REPLACE PROCEDURE GetInterestRate( p_id IN NUMBER DEFAULT NULL,
RC1 IN OUT SYS_REFCURSOR
)
....
begin
open rc1 for select * from interestRatesTable i join parametersInterest p on
i.interestName = p.Name
where i.idinterest = p_id
and p.Active ='A';
-- I would like to check if cursor have only one row.
--- if it has zero row, no result I got to raise an error
--- if it has more than one row, I got to raise to many interest rates!
-- how can I do this?
end;
Oracle will raise the errors for you, simply select a value into a row
I guess you should really catch the exception in an exception block too.
CREATE OR REPLACE PROCEDURE GetInterestRate( p_id IN NUMBER DEFAULT NULL,
RC1 IN OUT SYS_REFCURSOR
)
....
begin
select 1
into a_value
from interestRatesTable i
join parametersInterest p ...
open rc1 for ......
EXCEPTION
WHEN NO_DATA_FOUND THEN
.....
WHEN TOO_MANY_ROWS THEN
.....
WHEN OTHERS THEN
.....
end;
I think you'll have to run the query first before opening the cursor.
CREATE OR REPLACE PROCEDURE ...
begin
select count(*)
into row_found
from interestRatesTable i join parametersInterest p ...
and rownum = 1;
if row_found = 0 then raise ...
end if
open ...
end;