Q :
Display all doctors that their charge per
appointment is lower than the minimum.
If exists, display a message to ask these doctors to
increase their charge per appointment by 30%.
Otherwise, provide a message in exception
handlers to notify the user of such result.
Allow the user to enter the minimum charge per
appointment.
the exception doesnt work ??
ACCEPT minchg PROMPT 'Enter the minimum charge per appointment: RM '
DECLARE
ex_min := chgperappt > &minchg EXCEPTION;
v_id doctor.doc_id%TYPE;
v_name doctor.doc_name%TYPE;
v_chg doctor.chgperappt%TYPE;
CURSOR doc_chg IS
SELECT doc_id, doc_name, chgperappt
FROM doctor
WHERE chgperappt < &minchg;
BEGIN
OPEN doc_chg;
LOOP
FETCH doc_chg INTO v_id, v_name, v_chg;
EXIT WHEN doc_chg%NOTFOUND;
DBMS_OUTPUT.PUT_LINE ('Dr. '||v_name||' ('||v_id||') is charging RM '|| v_chg);
DBMS_OUTPUT.PUT_LINE ('Please increase the charge per appointment by 30% --> RM '||v_chg*1.3);
EXCEPTION
WHEN ex_min THEN`enter code here`
DBMS_OUTPUT.PUT_LINE ('All charge per appointment met the minimum criteria.') ;
END LOOP;
CLOSE doc_chg;
END;
/
can you help ?
The basic template for creating a user-defined exception, and handling it, is something like this:
(not your complete code example, but a snippet)
DECLARE
ex_min EXCEPTION;
BEGIN
// open cursor, etc.
IF chgperappt > &minchg THEN
RAISE ex_min;
END IF;
// other code to execute if no exception is raised.
EXCEPTION
WHEN ex_min THEN
// add you code here for exception handling case
END;
Here is a link to a tutorial that goes through the process step-by-step. Hope that helps.
Related
My whole intention to catch exception,WRONG parameter is NOT CATCHING exception.
Here is the code:
CREATE OR REPLACE PROCEDURE list_emp (p_emp_id IN employees.employee_id%TYPE,
p_dept_id IN employees.department_id%TYPE)
IS
CURSOR c1 IS
SELECT *
FROM EMPLOYEES
WHERE EMPLOYEE_ID=p_emp_id
AND DEPARTMENT_ID=p_dept_id;
emp_rec c1%ROWTYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO emp_rec;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
END LOOP;
CLOSE c1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No Record Found ');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('No Record Found ');
END;
When the cursor is opened and fetched with the wrong parameter that does not match any row from the corresponding table, the following line
EXIT WHEN c1%NOTFOUND;
cause the plsql procedure to terminate (because there were no rows found). Hence no exception is raised.
If you do want to display some sort of output you can do the following instead
IF c1%FOUND THEN
dbms_output.put_line('Record Found');
ELSE
dbms_output.put_line('Finished/Done');
EXIT;
END IF;
If you want to raise an error after looping through a cursor that returns no rows, then you're going to have to use a counter to work out how many rows have been processed, and then you can do something if no rows have been processed.
Something like:
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type)
is
cursor c1 is
select employee_id,
first_name,
last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
v_count number := 0;
begin
for emp_rec in c1
loop
v_count := v_count + 1;
dbms_output.put_line(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
end loop;
if v_count = 0 then
raise no_data_found;
end if;
exception
when no_data_found then
dbms_output.put_line('No Record Found.');
raise;
when others then
dbms_output.put_line('An error occurred: '||sqlerrm);
raise;
end;
/
A few notes:
I converted your cursor loop into a cursor-for-loop; you don't need to worry about declaring the record type and also Oracle handles the opening and closing of the cursor for you.
I added raise; to each of your exception handlers - in general, having when others then null (which is effectively what your original code was doing - no errors are raised to the calling code) is a bad idea. I added the raise to the no_data_found condition as that wasn't doing anything either; typically, if you have an exception condition, you want it to do something to let the calling code know there was a problem (not always, of course; sometimes you don't want the processing to stop if a particular error condition is met).
Your cursor was selecting all columns, but in your procedure, you were only using three of them. I've therefore amended the cursor so that it only pulls back those three columns.
Don't rely on dbms_output in your production code. Code that calls this procedure won't see anything populated in dbms_output, unless it explicitly looks for it - and that's not something I've ever seen in any production code, outside of Database tools (eg. SQL*Plus, Toad, etc). I've left this in your procedure as I've a feeling this is a learning exercise for you, but please don't think that this is in any way acceptable in production code.
You're passing p_emp_id in as a parameter - typically, that's the primary key of the employees table. If that's the case, then there's no need for the cursor for loop at all - you could do it by using select ... into ... instead, like so:
.
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type)
is
v_emp_id employees.employee_id%type;
v_first_name employees.first_name%type;
v_last_name employees.last_name%type;
begin
select employee_id,
first_name,
last_name
into v_emp_id,
v_first_name,
v_last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
dbms_output.put_line(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
exception
when no_data_found then
dbms_output.put_line('No Record Found.');
raise;
when others then
dbms_output.put_line('An error occurred: '||sqlerrm);
raise;
end;
/
Alternatively, just pass back a ref cursor:
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type,
p_ref_cur out sys_refcursor)
is
begin
open p_ref_cur for select employee_id,
first_name,
last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
-- No need for an exception handler here since you're not storing the error details anyway.
-- By not having an error handler, any error will automatically be raised up to the calling code
-- and it will have the correct error stack trace info (e.g. the line number the error occurred,
-- rather than the line the error was reraised from
end;
/
And to run the ref cursor in SQL*Plus (or as a script in Toad/SQL Developer/etc), you do the following:
-- create a variable outside of PL/SQL to hold the ref cursor pointer (this is a SQL*Plus command):
variable rc refcursor;
-- populate our ref cursor variable with the pointer. Note how we pass it in as a bind variable
begin
list_emp(p_emp_id => 1234,
p_dept_id => 10,
p_ref_cur => :rc);
end;
/
-- finally, print the contents of the ref cursor.
print rc;
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?
I had two buttons that do different job but there is check data entry in common
so I made a program unit for that check then I call it from these two buttons
but my problem is that when there is an error within the check I got the message for user and all things I made but the its get back to the code within button and continue progressing I set a return keywords at end of each condition to sop the code from running but its not working please whats the problem ? how I can stop my code until the error check passed ?!!
example of check data entry program unit
PROCEDURE CHECK_ENTRY IS
BEGIN
IF :block1.text_item1 IS NULL THEN
SET_ITEM_INSTANCE_PROPERTY('block1.text_item1',CURRENT_RECORD,VISUAL_ATTRIBUTE,'ERROR_ATR');
SHOW_MESSAGE('example msg .');
RETURN;
ELSIF :block2.text_item2 IS NULL THEN
SET_ITEM_INSTANCE_PROPERTY('block2.text_item2',CURRENT_RECORD,VISUAL_ATTRIBUTE,'ERROR_ATR');
SHOW_MESSAGE('example msg2 .');
RETURN;
END IF ;
END;
example of code within on_button_press trigger
PROCEDURE procedure_name IS
BEGIN
IF FORM_SUCCESS THEN
DISPLAY_ERROR;
:block1.text_item1:= :block2.text_item2;
:block2.text_item2:=:block1.text_item1;
**CHECK_ENTRY;** /* here is the calling of previous program unit that check the data entry then get back to here in case there is no error and continue commit the data and disabling text item so user wont be able to modify the data */
COMMIT_FORM;
program_unit('ORDER_DONE');
ELSE
ROLLBACK;
DISPLAY_ERROR;
SHOW_MESSAGE('please connect administrator.');
END IF;
END;
Your code should be like this:
PROCEDURE CHECK_ENTRY IS
BEGIN
IF :block1.text_item1 IS NULL THEN
SET_ITEM_INSTANCE_PROPERTY('block1.text_item1',CURRENT_RECORD,VISUAL_ATTRIBUTE,'ERROR_ATR');
SHOW_MESSAGE('example msg .');
raise form_trigger_failure;
ELSIF :block2.text_item2 IS NULL THEN
SET_ITEM_INSTANCE_PROPERTY('block2.text_item2',CURRENT_RECORD,VISUAL_ATTRIBUTE,'ERROR_ATR');
SHOW_MESSAGE('example msg2 .');
raise form_trigger_failure;
END IF ;
END;
PROCEDURE procedure_name IS
BEGIN
:block1.text_item1:= :block2.text_item2;
:block2.text_item2:=:block1.text_item1;
CHECK_ENTRY;
IF FORM_SUCCESS THEN
DISPLAY_ERROR;
COMMIT_FORM;
program_unit('ORDER_DONE');
ELSE
ROLLBACK;
DISPLAY_ERROR;
SHOW_MESSAGE('please connect administrator.');
END IF;
END;
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
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.