How to handle expection raised from CURSOR declaration - plsql

So I have a procedure below that accept a date as an IN parameter. If a wrong type of parameter is passed in, the exception area will not be able to catch it because the parameter is used in the CURSOR declaration section.
PROCEDURE ABC(p_date IN DATE)
IS
CURSTOR cur
IS
SELECT *
FROM table
WHERE table.date = TRUNC(p_date);
BEGIN
do something;
EXCEPTION
WHEN OTHERS THEN
do something;
END;
If the user passes a wrong parameter type:
BEGIN
ABC(123);
END;
I'll get the "wrong number or types of arguments in call to 'ABC'" error.
Is there a way to handle this error under this scenario?

Micklesh is correct. An error in calling the procedure isn't handled by the called procedure, it needs to be handled in the calling procedure.

Related

What EXCEPTION can be added to this function in PL/SQL?

This function is used to return the total number of orders for a shopper using their ID. Every time I try to throw an exception, nothing happens. It will return a null value. For example, IDSHOPPER = 19 does not exist within BB_BASKET, so I was thinking that I could throw an exception if lv_total_orders = 0, but it was still returning a null value and DBMS_OUTPUT.PUT_LINE would not show anything in the output. Could anyone suggest how to throw an exception for this function if I were to use a IDSHOPPER that does not exist in BB_BASAKET? This is for homework for my class, but I have done most of the work except for the exception which is required. I cannot find anything that suggests how to execute an exception.
CREATE OR REPLACE FUNCTION NUM_PURCH_SF
(p_shopper IN NUMBER)
RETURN NUMBER
AS
lv_total_orders NUMBER(3);
BEGIN
SELECT SUM(ORDERPLACED)
INTO lv_total_orders
FROM BB_BASKET
WHERE IDSHOPPER = p_shopper
AND ORDERPLACED = 1;
RETURN lv_total_orders;
END;
SELECT NUM_PURCH_SF(IDSHOPPER)
FROM BB_SHOPPER
WHERE IDSHOPPER = 23;
Aggregation functions always returns single row, even if no rows match the condition. You can look at this article
So, if you want to throw exception, you can use raising exceptions explicitly.
In your code, before return statement:
if lv_total_orders is null then
raise_application_error(-20200, 'No data found for given ShopperID, ShopperID: ' || p_shopper);
end if;

Highest record in PL/SQL

I have created a query that displays highest returned items from a table. My query it works perfectly with no errors! However, I want an efficient way of converting the query to PL/SQL block. The purpose of conversion is to handle errors.
SELECT ITEM_NO, MAX(QUANTITY) AS MAXIMUM
FROM ITEMS
WHERE CAT_NO >= (
SELECT MAX(ITEM_NUM) FROM ORDER
WHERE STATUS IN('ONE ITEM RETURNED','ALL ITEMS RETURNED')
)
GROUP BY ITEM_NO
ORDER BY ITEM_NO ASC;
Here's a way we do some exception handling in packages. I altered it to use your code as an example. Maybe you can use some ideas from it.
At the top, set a CONSTANT to the name of the procedure, and some variables to catch Oracle SQL error number and message in. Then in the body of the procedure, there is an anonymous block containing the select. First we set a variable to indicate where we are (err_loc) in case there are multiple locations where an error could be caught in one block. Then the select is issued. If an error occurs, it's caught by the EXCEPTION clause. Error info from Oracle is caught in the err* variables, the err_string is built and then emailed via the UTL_MAIL package. RAISE raises the error so the program halts. It's set up like this to be generic as possible, we can drop in the template, change the MBR_NAME, SQL, err_loc and that's it.
PROCEDURE TEST_PROC AS
MBR_NAME CONSTANT VARCHAR2(100) := 'TEST_PROC'; -- For use in error handling. Package member name.
err_nbr NUMBER; -- Holds a SQL error number if an exception occurs.
err_msg VARCHAR2(1000); -- Holds a SQL error message if an exception occurs.
err_string VARCHAR2(2000);
BEGIN
BEGIN
err_loc := 'Selecting max quantity'; -- Email subject
SELECT ITEM_NO, MAX(QUANTITY) AS MAXIMUM
FROM ITEMS
WHERE CAT_NO >= (SELECT MAX(ITEM_NUM)
FROM ORDER
WHERE STATUS IN('ONE ITEM RETURNED','ALL ITEMS RETURNED')
)
GROUP BY ITEM_NO
ORDER BY ITEM_NO ASC;
EXCEPTION
WHEN OTHERS THEN
err_nbr := SQLCODE;
err_msg := SUBSTR(SQLERRM, 1, 1000);
err_string := 'ERROR: ' || err_nbr || ' occurred: ' || err_msg;
-- PKG_NAME and err_email_recip set in the body.
UTL_MAIL.send(sender => PKG_NAME||'.'||MBR_NAME||'#yourcompany.com',
recipients => err_email_recip,
subject => 'ERROR '|| err_loc,
message => CHR(13))||err_string);
RAISE;
END;
END TEST_PROC;
Have a look at cursors and records.
That way you have fetch data from the query and process the line if needed.
I don't have a database by hand to test my code, but this might give you an idea how a cursor and record work.
In order to capture a EXCEPTION you could add an exception handler and let it log the record you where busy with when the exception occured.
DECLARE
CURSOR CursorName IS
SELECT ColumnOne
FROM TableA
WHERE Name = 'Me';
RecordNumber CursorName%ROWTYPE;
BEGIN
-- Fetch the records from the cursor.
OPEN CursorName;
LOOP
FETCH CursorName INTO RecordNumber;
-- Do something with the record.
EXIT WHEN CursorName %NOTFOUND;
END LOOP;
CLOSE CursorName;
END;
/
Adding a error handeling would be done right above END:
EXCEPTION
WHEN OTHERS THEN
-- Log error message.
END;
/
Link: Error handeling
Does that answer you question a bit?

Exception handling - plsql

I have a problem. Am doing an insert function
case f1.RFD_CATEGORY_CODE when'O1' then 'C1GBC'
when 'O2' then 'C2GBC' else null end
the field is mandatory and thus instead of null i need to show an error message if the code is not taking C1GBC or C2GBC. and if the code is taking C1GBC or C2GBC then show successful as message.
i have create an exception below but am getting error
create or replace procedure CTP_CODE as
declare
--RFD_CAT_ERR varchar2;
RFD_CAT_ERR EXCEPTION;
begin
if RFD_CATEGORY_CODE is '01' then RFD_CATEGORY_CODE is 'C1GBC';
DBMS_OUTPUT.PUT_LINE ('No1. Successful Operation');
else
if RFD_CATEGORY_CODE is '02' then RFD_CATEGORY_CODE is 'C2GBC';
DBMS_OUTPUT.PUT_LINE ('No2. Successful Operation');
end if;
raise RFD_CAT_ERR;
end if;
EXCEPTION
when RFD_CAT_ERR then
DBMS_OUTPUT.PUT_LINE ('Error message!');
end;
/
Wrong Code also wrong syntax..
Whenever your code goes in else condition it will throw error as after executing 2nd if it will go to "raise RFD_CAT_ERR;" part and raise an exception.
so u should handle error in elsif after 2nd If condition.
You say the field is mandatory. If by that you mean the field in the table is constrained as not null then you don't have to worry about raising an exception. The insert statement will do that for you. All you have to do is catch it in your exception handler and use RAISE_APPLICATION_ERROR to return a meaningful message.
Do that rather than try to print an error message. If your procedure is called by a batch process, there will be no one there to see it.
This works for me :D
CASE f1.RFD_CATEGORY_CODE
WHEN 'O1' THEN 'C1GBC'
WHEN 'O2' THEN 'C2GBC'
ELSE 'error'
Then raise any errors for exception

Oracle PL/SQL - ORA-01403 “No data found” when using “SELECT INTO”

I have a pl sql code that execute three queries sequentially to determine a match level and do some logic
The issue is - when first query has no results (completely valid scenario) I get ORA-01403 No data found.
I understand that I need to incorporate [ Exception clause when NO_DATA_FOUND ]- but how to add it and continue to the next query?
PL/SQL Code
SELECT A into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- GOT ORA-01403 No data found HERE
MATCH_LEVEL =1;
if A is null then
do some logic;
end if
SELECT A INTO PARAM_B FROM SAMPLE WHERE SOME OTHER CONDITION
MATCH_LEVEL =2
if A is null then
do some logic 2;
end if
SELECT A INTO PARAM_B FROM SAMPLE WHERE SOME OTHER CONDITION
MATCH_LEVEL =3
if A is null then
do some logic 3;
end if
END PL/SQL Code
Declare
--your declarations
begin
SELECT A into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- GOT ORA-01403 No data found HERE
Begin
MATCH_LEVEL =1;
if A is null then
do some logic;
end if;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line ('Error...');
END;
--- and son on for other blocks
end;
Just surround your SELECT INTO with begin-end;
begin
-- your faulty statement here
Exception
When NO_DATA_FOUND Then
-- Do what you want or nothing
WHEN TOO_MANY_ROWS THEN
-- what if you get more then one row? and need specific handler for this
When OTHERS Then
-- do something here or nothing (optional - may happen if you have more than your SELECT INTO between 'begin' and 'Exception')
end;
This is like try block of PL/Sql
With this technique you can log the reason your statement failed.
For a SELECT ... INTO ... statement, the PL/SQL engine assume there will be one, and only one row returned by your query. If there is no row, or more than one, an exception is raised.
FWIW, you can handle such cases without resorting on exception handling by using aggregate functions. That way, there will always be only one row in the result set.
Assuming A can't be NULL in your rows:
SELECT MAX(A) into PARAM A FROM SAMPLE WHERE SOME CONDITION;
-- A would be NULL if there was *no* row. Otherwise, it is *the* value for *the* row
MATCH_LEVEL =1;
if A is null then
do some logic;
end if
If the NULL value is a possible case, just add an extra COUNT(*) column:
SELECT MAX(A), COUNT(*) into A, HAS_FOUND_ROW FROM SAMPLE WHERE SOME CONDITION;
if HAS_FOUND_ROW > 0 then
...
end if;
Oracle will not allow you to open an implicit cursor (i.e. a select statement in the body of a code block) that returns no rows. You have two options here (3 really, counting #Sylvain's answer, but that is an unusual approach): use an explicit cursor or handle the error.
Explicit Cursor
An explicit cursor is one found in the DECLARE section it must be opened and fetched manually (or in a FOR loop). This has the added advantage that, if you parameterize the query properly, you can write it once and use it multiple times.
DECLARE
a sample.a%type;
MATCH_LEVEL number;
cursor cur_params (some_column_value number) is
SELECT A FROM SAMPLE WHERE some_column = some_column_value;
BEGIN
MATCH_LEVEL := 1;
open cur_params (match_level);
fetch cur_params into a;
close cur_params;
if A is null then
null; --some logic goes here
end if;
MATCH_LEVEL := 2;
open cur_params (match_level);
fetch cur_params into a;
close cur_params;
if A is null then
null; --some logic goes here
end if;
end;
Handle the error
If you choose to handle the error, you'll need to create a BEGIN...END block around the code that is going to throw the error. When disregarding an error, it's crucial that you ensure that you are only disregarding the specific error you want avoid, when generated from the specific statement you expect it from. If you simply add the EXCEPTION section to your existing BEGIN...END block, for instance, you couldn't know which statement generated it, or even if it was really the error you expected.
DECLARE
a sample.a%type;
MATCH_LEVEL number;
BEGIN
MATCH_LEVEL := 1;
BEGIN
SELECT A into A FROM SAMPLE WHERE some_column = MATCH_LEVEL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null; --Do nothing
END;
if A is null then
null; --some logic goes here
end if;
MATCH_LEVEL := 2;
BEGIN
SELECT A into A FROM SAMPLE WHERE some_column = MATCH_LEVEL;
EXCEPTION
WHEN NO_DATA_FOUND THEN
null; --Do nothing
END;
if A is null then
null; --some logic goes here
end if;
end;
While I'd discourage it, you can catch any other errors in the same exception blocks. However, by definition, those errors would be unexpected, so it would be a poor practice to discard them (you'll never know they even happened!). Generally speaking, if you use a WHEN OTHERS clause in your exception handling, that clause should always conclude with RAISE;, so that the error gets passed up to the next level and is not lost.

Unable to return query output in Apex PL SQL expression

I am trying to write a following PL/SQL function body for a dynamic action
The purpose of dynamic action is to set value for text area based on input parameters. Way I am trying to do it, is that setting the value into variable for different options
declare
P_NOTE varchar(100); -- derive value
P_WEBSERVER varchar(100); -- derive name
begin
-- for getting the P_NOTE value
select distinct note into P_NOTE from port_mapping where PLATFORM = :P3_PLATFORM and VERSION = :P3_VERSION;
-- for getting web server value
select CONCAT(P_NOTE,CONCAT('https-',:P3_CLIENT)) into P_WEBSERVER from dual order by 1;
if (:P3_PLATFORM = 'Apache') then
return P_WEBSERVER;
end if;
end;
However I am getting error
ORA-06550: line 15, column 5:
PLS-00372: In a procedure, RETURN statement cannot contain an expression
ORA-06550: line 15, column 5:
PL/SQL: Statement ignored
declare
P_NOTE varchar(100);
P_WEBSERVER varchar(100);
I am not sure what I am missing.
(Since you did not post any apex version this explanation deals with version 4.2)
If this -is- a dynamic action and the code you posted is in a true action of type 'Execute PL/SQL Code' then you can not use RETURN. The plsql block is not a function body (close, Mr Kemp!).
If you want to return values from the session state to page items then you need to use the "Page Items to Return" item of the true action.
This will put the session state of the defined page items into the value of the item on the page. This means that you can not use any variable to just put stuff in to be able to return it to the page, but you need to use an actual page item (after all, these are bind variables).
To clarify further, you would not write :
return P_WEBSERVER;
But you'd have to use a page item, say P3_WEBSERVER, and you'll need to create one if it doesn't exist of course:
:P3_WEBSERVER := p_webserver;
Of course you'd need to make sure that the correct value will be in there as you can not shortcircuit as you did in your code sample (p_webserver will usually hold a value even if the platform is not 'Apache') eg:
if (:P3_PLATFORM = 'Apache') then
:P3_WEBSERVER := P_WEBSERVER;
else
:P3_WEBSERVER := NULL;
end if;
Just read error message:
line 15, column 5
So, trouble caused by this line:
return P_WEBSERVER;
return not allowed in PL/SQL blocks, use output parameter to return a value.
Read Tom's answer to find out how to do that.

Resources