PL/SQL Exception handling and update statements - sql

I'm quite new to PL/SQL and I have a problem with displaying my exceptions that I can't seem to figure out.
I'm using two stored procedures and an anonymous block to call them. I've made a few other procedures in this manner and they all work just fine.
This is the first procedure that is updating my table.
create or replace procedure UPD_CUST_SALESYTD_IN_DB (pcustid number, pamt number) AS
err_pamt exception;
err_pcustid exception;
vcount number;
begin
select count(*) into vcount from customer where
custid = pcustid;
if vcount = 0 then raise err_pcustid;
end if;
if pamt <-999.99 or pamt >999.99 then raise err_pamt;
end if;
update customer set sales_ytd = sales_ytd + pamt
where custid = pcustid;
exception
when err_pcustid then
RAISE_APPLICATION_ERROR(-20031, 'Customer ID not found');
when err_pamt then
RAISE_APPLICATION_ERROR(-20032, 'Amount out of range');
when others then
RAISE_APPLICATION_ERROR(-20000, SQLERRM);
end;
This is the procedure that calls the above procedure. This is just displaying what I'm going to do and confirms that it worked.
create or replace procedure UPD_CUST_SALESYTD_VIASQLDEV (pcustid number, pamt number) AS
begin
dbms_output.put_line('--------------------------------------------');
dbms_output.put_line('Updating SalesYTD. Customer Id: ' || pcustid || ' Amount: ' || pamt);
UPD_CUST_SALESYTD_IN_DB(pcustid, pamt);
commit;
dbms_output.put_line('Udpate OK');
exception
when others then
RAISE_APPLICATION_ERROR(-20000, SQLERRM);
end;
This is the anonymous block I'm using to call the above procedure which then calls the one above that.
set serveroutput on;
begin
UPD_CUST_SALESYTD_VIASQLDEV(3,999.9);
end;
If I pass parameters that would not give me an error, all the code works just fine.
For example, if I input;
set serveroutput on;
begin
upd_cust_salesytd_viasqldev(3,400);
end;
I get the correct output and the changes have been made in the table.
--------------------------------------------
Updating SalesYTD. Customer Id: 3 Amount: 400
Udpate OK
However, if I pass parameters that would result in an error, either a customer id not existing or the amount being out of the range, nothing happens.
I get this:
--------------------------------------------
Updating SalesYTD. Customer Id: 3 Amount: 1000
Nothing else.
In similar procedures my exceptions are working just fine. This example is using a procedure that inserts into a table.
--------------------------------------------
Adding Customer. ID: 500 Name: Helen Nolan
ORA-20002: Customer ID out of range
I'm not sure why this procedure is not returning my exceptions at all. In my other procedures that work, if an exception is raised, the script output in Oracle SQL Developer just displays my exception.
However, in my update procedure, if something should raise an exception, above the dbms output lines, the script output prints this error report
Error report -
ORA-20000: ORA-20032: Amount out of range
ORA-06512: at "S4931645.UPD_CUST_SALESYTD_VIASQLDEV", line 10
ORA-06512: at line 2
20000. 00000 - "%s"
*Cause: The stored procedure 'raise_application_error'
was called which causes this error to be generated.
*Action: Correct the problem as described in the error message or contact
the application administrator or DBA for more information.
So it's acknowledging there that my exception has been raised, but isn't shown.
Any help is greatly appreciated, I'm thoroughly confused by this. Am I making some truly obvious mistake?

Not an expert here — just helping brainstorm. It sounds like you are getting a response but you just need to write a custom error statement. Out of range means that in the list that was selected with parameters set that it does not exist within the scope of that query. For example if list was 0-100 then response was from 0-10 and 0 was not found in that range.

Related

Continue loop when dblink SQL statement timeout occurs

I'm writing a procedure that loops over several remote Databases using dblink, I want to include statement timeout to prevent queries hanging too long. However if such timeout occurs, whole procedure fails with:
ERROR: canceling statement due to statement timeout
SQL state: 57014
Context: while executing query on dblink connection named xxx
I want to ignore it and continue the loop.
Normally such code allows to skip exception throwing notice only, but not with dblink query canceled.
do $$
declare
exceptionMsg text;
BEGIN
select * from foo;
exception when others then get stacked diagnostics exceptionMsg = message_text;
raise notice ' ******EXCEPTION*******
%
**************', exceptionMsg;
END;
$$
It's too long to include whole procedure here, but it loops over database and commits results after each database. Everything works fine, except handling these timeouts, part of the code looks like that:
for rec in (select dbc.db_name, dbc.con_string || ' options = ''-c statement_timeout='||_queryTimeout*1000||''' ' as con_string
from db_connections dbc
)
LOOP
PERFORM dblink_connect(rec.db_name, rec.con_string);
raise notice '% start', rec.db_name ;
BEGIN
insert into results_tbl (db_name, value, query_text)
select rec.db_name, value, _queryText
from dblink(rec.db_name, format($query$
select json_agg(x.*)::text from (%1$s)x -- it's like this to avoid declaring every column used in the query
$query$, _queryText
) ) r (value text);
exception when others then get stacked diagnostics exceptionMsg = message_text;
raise notice ' ******EXCEPTION*******
%
**************', exceptionMsg;
END;
PERFORM dblink_disconnect( rec.db_name );
COMMIT;
raise notice '% done', rec.db_name ;
END LOOP;
As documented,
The special condition name OTHERS matches every error type except QUERY_CANCELED and ASSERT_FAILURE.
So you need to capture QUERY_CANCELED explicitly.
Capturing OTHERS is bad style. Only capture the exceptions you expect.

Retrieve data by user input with a message (PL/SQL)

I want to how to retrieve data from the table I have added here by inserting C_Id as the user input with defined variables and exceptions. If any customer is not available it has to display a message showing "No customer found". Please help me to understand this one.
Thank you!
If it is a stored procedure (should be; it accepts a parameter) and you just want to display what you found, then this might be one option:
create or replace procedure p_test (par_c_id in customer_details.c_id%type)
is
l_row customer_details%rowtype;
begin
select *
into l_row
from customer_details
where c_id = par_c_id;
dbms_output.put_line(l_row.customer_name ||', '||
l_row.address ||', '||
to_char(l_row.date_of_joined, 'dd.mm.yyyy')
);
exception
when no_data_found then
dbms_output.put_line('No customer found');
end;
/
Then run it as
set serveroutput on
begin
p_test(121);
end;
/
What does it do?
accepts a parameter
declares local variable which is equal to table's rowtype
select everything into the variable
using dbms_output.put_line, display elements you want
if nothing has been found, exception (no_data_found) is raised and handled in a way that you display appropriate message
Note that such an option works if tool you use (e.g. SQL*Plus, SQL Developer, TOAD, ...) allow displaying result of dbms_output.put_line. Otherwise, if you use e.g. Oracle Apex, procedure will still work, but you won't see anything.

Oracle stored procedure not working PLS-00306

i have this questions i am trying to solve and find below what i have solved so far. although the stored procedure haveno error but calling it i get this error :
ERROR at line 2: ORA-06550: line 2, column 3: PLS-00306: wrong
number or types of arguments in call to 'PUB_JOB_COUNT' ORA-06550:
line 2, column 3: PL/SQL: Statement ignored
Requirement:
Create a stored PL/SQL procedure object in the database. The procedure
should insert the publisher’s name, city, telephone number and the
number (count) of jobs he/she requested in the table PublisherDetails
for each Publisher who requested less than three print jobs otherwise
the procedure should display on the screen the publisher name followed
by job number, job start date and job completion date for each job
he/she requested. Screen output (hint: use the concatenation operator
‘||’) should be in the following format:
Please someone help me out please?
Publisher Name: Addison-Wesley
JobNo Start Date Completion Date
12 17-JAN-14 25-JAN-14
14 28-FEB-14 01-APR-14
Finally, a NO-DATA-FOUND exception should be catered for in the
EXCEPTION section and a message displayed on the screen (hint: use
DBMS_OUTPUT.put_line procedure provided by Oracle) informing the user
if such an error arises. Note that in order for DBMS_OUTPUT.put_line
to work in SQL*Plus, you should set SERVEROUTPUT on first. You should
check if the procedure executes properly by invoking the procedure and
checking the content of the PublisherDetails table. Do the following:
a) Create a script file with the necessary code to create the table
PublisherDetails and the PL/SQL procedure in the database; b) Create a
second script file with the following: • An SQL statement that clears
the content of the table PublisherDetails; • A PL/SQL anonymous block
statement to invoke (execute) the PL/SQL procedure; • A SELECT
statement to select all the records in PublisherDetails table.
my tables
publisher(publisherName, publisherCity, phoneNo)
pk
printJob(JobNo, startDate, complitionDate, publisherName)
pk fk(publisher)
publisherdetails(publisherName, publisherCity, phoneNo, JobNo)
pk
Code:
CREATE OR REPLACE PROCEDURE PUB_JOB_COUNT (
JOBNO IN NUMBER
) AS
PUBLISHERNAME PRINTJOB.PUBLISHERNAME%TYPE;
NOTFOUND EXCEPTION;
CURSOR PUBCURSOR IS
SELECT PUBLISHER.PUBLISHERNAME,
PUBLISHER.PUBLISHERCITY,
PUBLISHER.PHONENO,
PRINTJOB.STARTDATE,
PRINTJOB.COMPLETIONDATE,
SUM(JOBNO) AS NUMOFJOBS
FROM PUBLISHER
INNER JOIN PRINTJOB ON PUBLISHER.PUBLISHERNAME = PRINTJOB.PUBLISHERNAME
GROUP BY PUBLISHER.PUBLISHERNAME,
PUBLISHER.PUBLISHERCITY,
PUBLISHER.PHONENO,
PRINTJOB.STARTDATE,
PRINTJOB.COMPLETIONDATE;
PUBREC PUBCURSOR%ROWTYPE;
BEGIN
OPEN PUBCURSOR;
FOR PRINTJOB IN PUBCURSOR LOOP
PUBLISHERNAME := PRINTJOB.PUBLISHERNAME;
DBMS_OUTPUT.PUT_LINE('Publisher Name : ' || PRINTJOB.PUBLISHERNAME);
LOOP
FETCH PUBCURSOR INTO PUBREC;
EXIT WHEN PUBCURSOR%NOTFOUND;
IF PUBREC.NUMOFJOBS <= 3 THEN INSERT INTO PUBLISHERDETAILS VALUES (
PUBREC.PUBLISHERNAME,
PUBREC.PUBLISHERCITY,
PUBREC.PHONENO,
PUBREC.NUMOFJOBS
);
ELSE DBMS_OUTPUT.PUT_LINE(PUBREC.NUMOFJOBS
|| ' '
|| PUBREC.STARTDATE
|| ' '
|| PUBREC.COMPLETIONDATE);
END IF;
END LOOP;
END LOOP;
CLOSE PUBCURSOR;
COMMIT;
EXCEPTION
WHEN NOTFOUND THEN DBMS_OUTPUT.PUT_LINE('Record Not Found');
END;
Gleaned from comments below, the code being used to execute the procedure:
BEGIN
pub_Job_Count;
End;
Your program is expecting an input, a number.
But when you call it, you're not providing said number.
So, the database gets upset and issues this:
PLS-00306: wrong number or types of arguments in call to 'PUB_JOB_COUNT'
To fix it,
BEGIN
pub_Job_Count(1); -- your number is added here, either explicitley or via a variable you add in a DECLARE
END;
/
A basic example
clear screen
create or replace procedure so_missing_inputs (x in integer, y in date) is
begin
dbms_output.put_line('X is: ' || x);
dbms_output.put_line('Y is: ' || to_char(y, 'MM-DD-YYYY HH24:MI:SS'));
end;
/
set serveroutput on
declare
a integer := 2;
b date := sysdate;
begin
-- so_missing_inputs(); -- missing 2 inputes
-- so_missing_inputs(1); -- missing 1 inputs
-- so_missing_inputs(sysdate, 2); -- right number of inputs, but not right data types
so_missing_inputs(x => a, y => b); -- let's be explicit about inputs vs coutning on right order
end;
/
If I run this -
If you were to uncomment one of the previous lines, you'd see the PLS-00306 creep back in.
One final note, on DBMS_OUTPUT. It's a good way to see what things are happening while 'debugging' your code, but it's not a good way to communicate things outside the PL/SQL program.

Oracle Stored Procedure 'Learnings' issue

I have been tasked (as part of an assignment) to write a stored procedure in Oracle PL/SQL. There are 3 requirements that have to be met.
There must be 2 parameters, 1 IN and 1 OUT.
I must use an implicit cursor and SQL function to calculate a count of the numbers of fields of the same type (in this case the type is car models, so how many cars of each model are there).
I must use another implicit cursor to display the description of the models.
To be honest, I am at a loss. So far for the stored proc I have:
CREATE OR REPLACE Procedure model_details_sp
(p_model IN VARCHAR2,
p_noofcars OUT NUMBER)
IS
BEGIN
SELECT COUNT(Model_Name) INTO p_noofcars
FROM i_car
GROUP BY Model_Name;
END;
I really have no idea where to go from here. Any advice or direction would be most appreciated.
Many thanks.
Hi guys I appreciate all the comments. I wasn't very clear with the end requirements. I want to be able to call this procedure via an anonymous block so that the user will enter a model type (&vairalbe) and the procedure will display how many of that model types are in the database.
When dealing with this type of problems, first think about the data you're trying to capture.
Dealing with implicit cursors in PL/SQL require 1-row, so you need to make sure you understand the data.
In this case, you pass in a variable that you don't use in any of your queries, so I suggest you re-evaluate.
I don't have a database at hand to run this, but you should be able to work this out and hopefully get you a bit closer. I put it in an anonymous block so that I can write it really quick.
DECLARE
PROCEDURE model_details_sp (p_model IN VARCHAR2, p_noofcars OUT NUMBER)
IS
p_description VARCHAR2 (200);
BEGIN
--2
SELECT COUNT (model_name)
INTO p_noofcars
FROM i_car
WHERE model_name = p_model;
DBMS_OUTPUT.put_line ('No of Cars for model: ' || p_noofcars);
--3
SELECT model_description
INTO p_description
FROM i_car --the table should be the car_model table so that only one record is returned
WHERE model_name = p_model;
DBMS_OUTPUT.put_line ('Model Desc' || p_description);
END model_details_sp;
BEGIN
dbms_output.put_line('');
END;
To #David Aldridge comment:
Try running this--the result should be a failure--as you cannont select multiple rows using the into CLAUS, unless you aggregate the data:
DECLARE
p_num NUMBER;
BEGIN
SELECT LEVEL INTO p_num FROM DUAL CONNECT BY LEVEL <= 10;
dbms_output.put(p_num);
END;
The error you should see is this:
Error report:
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 4
01422. 00000 - "exact fetch returns more than requested number of rows"
*Cause: The number specified in exact fetch is less than the rows returned.
*Action: Rewrite the query or change number of rows requested
From the description and subsequent comments, this is the solution I would provide:
DECLARE
PROCEDURE model_details_sp
(p_model IN VARCHAR2,
p_noofcars OUT NUMBER)
IS
BEGIN
SELECT COUNT(*)
INTO p_noofcars
FROM i_car
WHERE model_name = p_model;
END;
no_of_cars NUMBER := 0;
BEGIN
model_details_sp(:model_name, no_of_cars);
dbms_output.put_line('no of cars for ' || :model_name || ' = ' || no_of_cars);
END;
I've created the PROCEDURE in-line but you can just as easily extract this to the database by removing it from the declare section and executing it with CREATE OR REPLACE.
This example assumes use of an IDE that supports bind variable replacement (:model_name) on execute of the anonymous block. In TOAD, for example, the "user" will be prompted to provide a value for :model_name.

PL/SQL : Encountered the symbol "END"

I get the following error, even though I have checked the code carefully. I don't know what I'm missing.
LINE/COL ERROR
31/1 PLS-00103: Encountered the symbol "END" when expecting one of the
following:
CODE:
CREATE OR REPLACE PROCEDURE sp_ssjm_newworkorder
( workorderno IN NUMBER,
company IN CHAR,
attention IN CHAR,
datedue IN DATE,
loggedby IN CHAR
)
AS id NUMBER;
today DATE:=SYSDATE;
BEGIN
SELECT client_id --grab client_id
INTO id
FROM ssjm_client
WHERE ssjm_client.name=company;
IF id IS NULL THEN --check if client exists by checking if client_id is there
dbms_output.put_line('Please create client first');
GOTO the_end;
ELSE
INSERT INTO ssjm_workorder VALUES(workorderno,workorderno,company,loggedby,attention,'Received',today,datedue,id);
END IF;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20999,'An error occured in' ||
SQLCODE || '-ERROR-' || SQLERRM);
<<the_end>>
END sp_ssjm_newworkorder;
There are several spots in your code needed attention:
The reason you've got that error is because label <<the_end>> should be placed before the EXCEPTION section.
Operator is required after a label. So if you want to jump to the end of a stored procedure and no other actions required NULL operator should be used.
To that end your code should look like this:
IF id IS NULL THEN --check if client exists by checking if client_id is there
dbms_output.put_line('Please create client first');
GOTO the_end;
ELSE
INSERT INTO ssjm_workorder
VALUES(workorderno,workorderno,company,loggedby
,attention,'Received',today,datedue,id);
END IF;
<<the_end>>
NULL;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20999,'An error occured in' ||
SQLCODE || '-ERROR-' || SQLERRM);
END sp_ssjm_newworkorder;
By all means try to avoid unconditional branching. Using GOTO operator is very, very not good practice. It kills readability, code like that hard to debug. It will cause you and everybody who will look at that code after you a headache. Moreover if the query
SELECT client_id --grab client_id
INTO id
FROM ssjm_client
WHERE ssjm_client.name=company;
returns no rows the exception NO_DATA_FOUND will be immediately raised and execution of the code halts. So IF id IS NULL THEN condition will never be evaluated. You may rewrite your code by removing that condition and adding NO_DATA_FOUND exception handler in the EXCEPTION section of your code. And of course as #Rob van Wijk correctly pointed out in the comment
but code can be cleaned up further. today variable can be removed and
the WHEN OTHERS should definitely be removed. As it is now, it just
transforms an error to a longer error message without more detail and
most importantly: it disguises the line number where the real error
took place.
there is no need of today variable, SYSDATE can be used directly in the values clause of the insert statement, and WHEN OTHERS can be removed as well.
CREATE OR REPLACE PROCEDURE sp_ssjm_newworkorder
(
workorderno IN NUMBER,
company IN CHAR,
attention IN CHAR,
datedue IN DATE,
loggedby IN CHAR
)
AS
id NUMBER;
BEGIN
SELECT client_id --grab client_id
INTO id
FROM ssjm_client
WHERE ssjm_client.name=company;
INSERT INTO ssjm_workorder
VALUES(workorderno,workorderno,company,loggedby
,attention,'Received',SYSDATE,datedue,id);
EXCEPTION
when NO_DATA_FOUND
then dbms_output.put_line('Please create client first');
END sp_ssjm_newworkorder;