DBMS Pl/SQL -- What would be output ... please explain? - oracle

SQL> DESC hostel;
Name Null? Type
------------------------------------ -------- -------------------
HOSTELID NOT NULL VARCHAR2(4)
ROOMSAVAILABLE NUMBER(3)
HOSTELTYPE VARCHAR2(1)
HOSTELFEE NUMBER(6)
SQL> SELECT * FROM hostel;
HOST ROOMSAVAILABLE H HOSTELFEE
------- ---------------------- ---- ---------------------
H1 2 M 2000
H2 3 F 3000
Above is shown a table hostel and values in it.
What would be the output of following pl/sql program?
please explain in detail.
CREATE OR REPLACE PROCEDURE sp_validatehostelid
(p_hostelid IN hostel.hostelid%TYPE,
p_hostelfee OUT hostel.hostelfee%TYPE
)
IS
v_count NUMBER;
v_hostelfee hostel.hostelfee%TYPE;
BEGIN
SELECT COUNT(*) INTO v_count FROM hostel WHERE hostelid=p_hostelid;
IF v_count=0 THEN
RAISE_APPLICATION_ERROR(-20000,'Invalid Hostel id');
ELSE
SELECT hostelfee INTO v_hostelfee FROM hostel WHERE hostelid=p_hostelid;
DBMS_OUTPUT.PUT_LINE('Hostel Fee:'||v_hostelfee);
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No data found');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Other Errors in Procedure');
END sp_validatehostelid;
Procedure created.
DECLARE
g_hostelfee hostel.hostelfee%TYPE;
BEGIN
sp_validatehostelid('H5',g_hostelfee);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Other Errors in Block');
END;

"What will be the output? given that there is no row having hostelid =
'H5'"
Assuming you run this in a client with serveroutput enabled the output will be
Other Errors in Procedure
PL/SQL procedure successfully completed.
SQL>
Why?
The first select statement is a count, which cannot hurl a NO_DATA_FOUND exception.
The next line raises a user-defined exception, -20000.
This passes control to the exception handler block. -20000 is not NO_DATA_FOUND so the WHEN OTHERS clause is executed, which displays the message above.
The exception handler does not raise an exception itself, which is very bad practice. So the flow returns to the calling block.
Because no exception was found the calling block thinks the called procedure executed successfully, and so processing terminate cleanly. That's why it is bad practice not re-raise exceptions.
Note that if you run this without enabling serveroutput first the output will be:
PL/SQL procedure successfully completed.
SQL>

CREATE OR REPLACE PROCEDURE sp_validatehostelid
(
p_hostelid IN hostel.hostelid%TYPE,
p_hostelfee OUT hostel.hostelfee%TYPE
)
IS
v_count NUMBER;
v_hostelfee hostel.hostelfee%TYPE;
BEGIN
/* Count rows in 'hostel' table for given ID */
SELECT COUNT(*) INTO v_count FROM hostel WHERE hostelid=p_hostelid;
/* If there is noting in the table */
IF v_count=0 THEN
/* raise exception */
RAISE_APPLICATION_ERROR(-20000,'Invalid Hostel id');
ELSE
/* select fee from the 'hostel' table */
SELECT hostelfee INTO v_hostelfee FROM hostel WHERE hostelid=p_hostelid;
/* print the fee */
DBMS_OUTPUT.PUT_LINE('Hostel Fee:'||v_hostelfee);
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No data found');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Other Errors in Procedure');
END sp_validatehostelid;
DECLARE
g_hostelfee hostel.hostelfee%TYPE;
BEGIN
sp_validatehostelid('H5',g_hostelfee);
/*
**Here something should be done with 'g_hostelfee' variable
*/
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Other Errors in Block');
END;
If there is a row with hostelid = 'H5' collect the fee for given ID, print it and pass it out.
NOTE: It will work only for one row per ID. If there is more than one. TO_MANY_VALUES exception will be raised.

Related

Exception error when creating procedure oracle sql developer

i have been whacking my brain for the past 2 hours can't find a solution to this error. I am creating a simple procedure to find an employee. PL/SQL keeps giving me error. What is the problem ? what am i doing wrong here ?
This is my Procedure:
create or replace PROCEDURE find_employee (employeeNo IN number) as
INVALID_ID exception;
TOO_MANY_ROWS exception;
res number;
BEGIN
dbms_output.enable;
Select count(*) into res from employee where ID=employeeNo;
if (res>1)then -- Checking that the total count of the employee is 1 or not
raise TOO_MANY_ROWS; -- if greater then 1 then it raise TOO_MANY_ROWS error
ELSE IF (NOT EXISTS (Select ID from employee where ID=employeeNo)) -- Checking that the employeeNo user passes exist or not
then
raise INVALID_ID; -- if employeeNo doesnot exit then display invalid id message
ELSE
Select* from Employee where ID=employeeNo; -- else return employee info whose id==employeeNo
END IF;
EXCEPTION
when TOO_MANY_ROWS then
DBMS_OUTPUT.PUT_LINE ('Too many Rows with same employee id');
when INVALID_ID then
DBMS_OUTPUT.PUT_LINE ('Invalid employee id');
END find_employee;
And error is this:
Error(15,1): PLS-00103: Encountered the symbol "EXCEPTION" when expecting one of the following: ( begin case declare end exit for goto if loop mod null pragma raise return select update while with <an identifier> <a double-quoted delimited-identifier> <a bind variable> << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge
Error(20,18): PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: end not pragma final instantiable order overriding static member constructor map
Please God Help me :'(
You're missing END IF (line #16). it is easier to spot it if you write formatted code (nested IF should have been indented).
SQL> create or replace PROCEDURE find_employee (employeeNo IN number) as
2 INVALID_ID exception;
3 TOO_MANY_ROWS exception;
4 res number;
5 BEGIN
6 dbms_output.enable;
7 Select count(*) into res from employee where ID=employeeNo;
8 if (res>1)then -- Checking that the total count of the employee is 1 or not
9 raise TOO_MANY_ROWS; -- if greater then 1 then it raise TOO_MANY_ROWS error
10 ELSE IF (NOT EXISTS (Select ID from employee where ID=employeeNo)) -- Checking that the employeeNo user passes exist or not
11 then
12 raise INVALID_ID; -- if employeeNo doesnot exit then display invalid id message
13 ELSE
14 Select* from Employee where ID=employeeNo; -- else return employee info whose id==employeeNo
15 END IF;
16 END IF; --> this is missing
17 EXCEPTION
18 when TOO_MANY_ROWS then
19 DBMS_OUTPUT.PUT_LINE ('Too many Rows with same employee id');
20 when INVALID_ID then
21 DBMS_OUTPUT.PUT_LINE ('Invalid employee id');
22 END find_employee;
As #Dornaut commented, that code probably isn't the best one could produce. Here's another option; see if it helps.
CREATE OR REPLACE PROCEDURE find_employee (employeeNo IN NUMBER)
AS
res NUMBER;
e_row employee%ROWTYPE;
BEGIN
SELECT *
INTO e_row
FROM employee
WHERE id = employeeNo;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line ('Invalid employee ID');
WHEN TOO_MANY_ROWS
THEN
DBMS_OUTPUT.put_line ('Too many rows with same employee ID');
END find_employee;
So: if SELECT returns NO_DATA_FOUND or TOO_MANY_ROWS, it'll be handled. Otherwise, it'll fetch the whole row into a variable.

why it is working without PRAGMA AUTONOMOUS_TRANSACTION

I have a misunderstanding regarding to
PRAGMA AUTONOMOUS_TRANSACTION
directive.
As far as I know, it is used in logging or auditing procedure, to run independently by the main program (autonomous, procedure, function or trigger).
I have an UPDATE on a table which generated DUP_VAL_ON_INDEX. In this exception, I call a logging procedure which logs the error into a table. In the logging procedure I didn't specified PRAGMA AUTONOMOUS_TRANSACTION directive but it still makes the insert in my logging table.
Here is my code:
create table TEST_PRAGMA
( COL_1 number primary key
, COL_2 number
);
--
insert into TEST_PRAGMA values (1, 200);
insert into TEST_PRAGMA values (2, 200);
--
create table T_LOG
( msg_num number primary key
, MSG_DATE timestamp(6)
, INFO_MSG varchar2(10)
, LONG_MSG varchar2(100)
);
--
create sequence SEQ_TEST start with 1 increment by 1 nocache nocycle;
Package:
create or replace package pkg_logging as
procedure PRC_LOG ( P_MSG_NUM number
, P_MSG_DATE timestamp
, P_INFO_MSG varchar2
, p_long_msg varcahr2);
end PKG_LOGGING;
--
create or replace package body pkg_logging as
procedure PRC_LOG ( P_MSG_NUM number
, P_MSG_DATE timestamp
, P_INFO_MSG varchar2
, P_LONG_MSG VARCHAR2)
as
begin
insert into T_LOG
( MSG_NUM
, MSG_DATE
, INFO_MSG
, LONG_MSG
)
values
( P_MSG_NUM
, P_MSG_DATE
, P_INFO_MSG
, P_LONG_MSG
);
commit;
EXCEPTION
when OTHERS then
rollback;
RAISE_APPLICATION_ERROR(-20000, 'other error has occured: ' || sqlcode || ' - ' || sqlerrm);
end PRC_LOG;
end PKG_LOGGING;
--
set SERVEROUTPUT on;
begin
update TEST_PRAGMA set COL_1 = 1 where COL_2 = 200;
commit;
EXCEPTION
when DUP_VAL_ON_INDEX then
dbms_output.put_line ('DUP_VAL_ON_INDEX error has occured');
PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'error', 'test de logging');
rollback;
end;
Because I didn't specified the PRAGMA directive, I was expecting not to log the error even if the logic is correct.
Can anyone explain me why it is still logs my error and provide a sample where it does not log the code if I do not specify the PRAGMA AUTONOMOUS_TRANSACTION directive, please?
Thank you,
Can anyone explain me why it is still logs my error and provide a
sample where it does not log the code if I do not specify the PRAGMA
AUTONOMOUS_TRANSACTION directive, please?
The error is being Inserted in Log table since you are handling it as an Exception handling. You need to understand the behavior of AUTONOMOUS transaction as being an Independent piece of code which executes even if the main calling proc/pkg fails. It's not being handled as a part of Exception Handling. As shown in below demo you can see proc marked as AUTONOMOUS is called in BEGIN block directly rather than in Exception block to understand the behavior.
DECLARE
l_salary NUMBER;
--Private Proc marking as Autonomous transaction
procedure nested_block ‬
as
pragma AUTONOMOUS_TRANSACTION;
BEGIN
UPDATE emp
SET salary=salary+15000
WHERE emp_no=1002;
COMMIT;
END;‭
--Main Block ‬
BEGIN
SELECT salary
INTO l_salary
FROM emp
WHERE emp_no=1001;
Dbms_output.put_line('Before Salary of 1001 is'||l_salary);
SELECT salary
INTO l_salary
FROM emp WHERE emp_no=1002;
Dbms_output.put_line('Before Salary of 1002 is '|| 1_salary);
UPDATE emp
SET
salary = salary + 5000
WHERE emp_no = 1001;
--Calling Autonomous transaction
nested_block;
--And rolling back previous updates.
ROLLBACK;
SELECT salary INTO
l_salary
FROM emp
WHERE emp_no = 1001;
dbms_output.put_line('After Salary of 1001 is'|| l_salary);
SELECT salary
INTO l_salary
FROM emp
WHERE emp_no = 1002;
dbms_output.put_line('After Salary of 1002 is ' || l_salary);
end;
Output:
The output will have the Update done in Autonomous transaction. Updates done in the main block will be rolledback but not the one which is done in private proc marked as Autonomous
Before Salary of 1001 is 15000
Before Salary of 1002 is 10000
After Salary of 1001 is 15000
After Salary of 1002 is 25000
PKG_LOGGING.PRC_LOG() has a commit statement so it will commit.
Suppose your code looked liked this:
set SERVEROUTPUT on;
begin
insert into TEST_PRAGMA values (3, 300);
PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'info', 'inserted a record');
update TEST_PRAGMA set COL_1 = 1 where COL_2 = 200;
commit;
EXCEPTION
when DUP_VAL_ON_INDEX then
dbms_output.put_line ('DUP_VAL_ON_INDEX error has occured');
PKG_LOGGING.PRC_LOG(SEQ_TEST.NEXTVAL, systimestamp, 'error', 'test de logging');
rollback;
end;
How many records would you have in TEST_PRAGMA? Three. Because the insert was committed when we called PKG_LOGGING.PRC_LOG(), and consequently the rollback in the exception handler had no effect. And that's why we should use PRAGMA AUTONOMOUS_TRANSACTION in audit and logging routines: so we can successfully persist our logging messages without affecting the broader transaction.
So you should add PRAGMA AUTONOMOUS_TRANSACTION to PKG_LOGGING.PRC_LOG().
Incidentally, I think you should be careful with an error handler like this in a logging package:
EXCEPTION
when OTHERS then
rollback;
RAISE_APPLICATION_ERROR(-20000, 'other error has occured: ' || sqlcode || ' - ' || sqlerrm);
end PRC_LOG;
In some situations we definitely would want to stop our process if we can't log vital information. But other times we want logging to fail gracefully. For instance I need the overnight batch run to abend if it can't record errors, because that log is my only way of knowing what - if anything - went wrong, and it's better for the whole thing not to run that for it to run incompletely and me not to know that some things failed. But if I'm just writing some trace messages in Test I might prefer the long running process to conclude without a complete set of trace rather than abend because the logging table has run out of space.
Also, using raise_application_error() is not necessary. Just issue raise; after the rollback and be done with it.

How to raise exception in procedures?

This is my procedure:
create or replace procedure p1(p_deptno in number)
is
cursor c is select * from emp where deptno=p_deptno;
i emp%rowtype;
begin
open c;
loop
fetch c into i;
exit when c%notfound;
dbms_output.put_line(i.ename);
end loop;
exception
when no_data_found then
dbms_output.put_line('Give proper deptno');
end p1;
/
When I run it using SQL*Plus, I get this:
SQL> exec p1(70);
PL/SQL procedure successfully completed.
But deptno 70 is not available. Exception should be raised, but it is not. What am I doing wrong?
Using RAISE_APPLICATION_ERROR Will be more appropriate here if we need to show a user defined error for this particular error message.
CREATE OR REPLACE PROCEDURE p1(
p_deptno IN NUMBER)
IS
i emp%rowtype;
BEGIN
SELECT * INTO i FROM emp WHERE deptno=p_deptno;
EXCEPTION
WHEN no_data_found THEN
RAISE_APPLICATION_ERROR(-20001,'Dept no '||p_deptno||' has no data',TRUE);
END p1;
No data found exception would be raised only if you fire a select (not a cursor). Following is a sample based on your code where this would happen:
create or replace procedure p1(p_deptno in number)
is
i emp%rowtype;
begin
select * into i
from emp where deptno=p_deptno;
exception
when no_data_found then
dbms_output.put_line('Give proper deptno');
end p1;
/
You could raise an exception based on %NOTFOUND as demonstrated by Gavin.
Also,the first fetch from an open cursor, cursor_name%NOTFOUND returns NULL.Thereafter, it returns FALSE if the last fetch returned a row, or TRUE if the last fetch failed to return a row. (https://docs.oracle.com/cd/B12037_01/appdev.101/b10807/13_elems011.htm)
You need to call RAISE (documentation link here)
IF i IS NULL THEN
RAISE no_data_found;
END IF;

Procedure to delete non existing rows oracle

I wrote a procedure in PL/SQL to delete rows from a table,However,if that record does not exist,then throws some error like this: DBMS_OUTPUT.PUT_LINE('No such record'); My procedure is:
CREATE OR REPLACE PROCEDURE del_cn2
(c_cntry_id IN COUNTRIES.COUNTRY_ID%TYPE
)
IS
v_error_code NUMBER;
BEGIN
DELETE from countries
WHERE country_id =c_cntry_id;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('No such record');
END IF;
EXCEPTION WHEN OTHERS THEN
v_error_code :=SQLCODE;
IF v_error_code =-2292 THEN
RAISE_APPLICATION_ERROR(-20004,'Organization '||TO_CHAR(c_cntry_id)||' site
details defined for it.');
END IF;
END;
/
However,when I execute this procedure and provide a record that does not exist in my table,it gives message "Procedure completed successfully" I am using this to execute:
Execute procedure del_cn2('JJ');
Can someone please suggest?
If you want an exception to be thrown when a value that does not exist in the table is passed in, you would need to actually throw an exception. You shouldn't use dbms_output for any sort of error output. That is a very simplistic debugging tool-- you shouldn't assume that the caller will ever be able to see that output.
My guess is that you want something like
CREATE OR REPLACE PROCEDURE del_cn2
(c_cntry_id IN COUNTRIES.COUNTRY_ID%TYPE
)
IS
BEGIN
DELETE from countries
WHERE country_id =c_cntry_id;
IF SQL%ROWCOUNT = 0
THEN
raise_application_error( -20001, c_cntry_id || ' no such value.' );
END IF;
END;
try to set serverout to ON
example:
create table tst_delete (col1 int);
create procedure p_test_delete as
BEGIN
DELETE FROM tst_delete
WHERE col1 = 1;
IF (SQL%NOTFOUND)
THEN
dbms_output.put_line('No records found');
END IF;
END;
then call the procedure in SqlPlus
SQL> exec p_test_delete;
PL/SQL procedure successfully completed
same issue that you described - no insformation...
next try with output activated
SQL> set serverout on
SQL> exec p_test_delete;
No records found
PL/SQL procedure successfully completed
SQL>

PL/SQL anonymous block completed with no result displayed

When I execute the procedure nth would appear except the following message and not showing any output which should not be the case: anonymous block completed
I've tried to set serveroutput on size 50000 but nothing changes.
Here's my PL/SQL procedure not sure if i'm doing it right.
CREATE OR REPLACE PROCEDURE verify AS
empnum NUMBER;
empname VARCHAR2(50);
fail EXCEPTION;
BEGIN
SELECT employee.e#, employee.name INTO empnum, empname
FROM employee
JOIN driver ON driver.e# = employee.e#
JOIN mechanic ON mechanic.e# = driver.e#
WHERE rownum = 1;
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('ok');
RAISE fail;
END verify;
/
I'm trying to achieve the following result in using sql statement:
SELECT employee.e#, employee.name
FROM employee
JOIN driver ON driver.e# = employee.e#
JOIN mechanic ON mechanic.e# = driver.e#
WHERE rownum = 1;
So if there's any similar records it will show the employee name and num. If there's no similar records found it will display a ok message.
add dbms_out_put.put_line(empnum||empname);
before the EXCEPTION.
try it/
You are just selecting the column values into the variables and not displaying them. Use DBMS_OUTPUT.put_line('Empnum: '|| empnum||' Empname: '||empname); just after the Select statement.
Can you please signify the FAIL variable you have used to
Raise Exception. We can do it without using this variable. This
is reference procedure.Plz try and let us know if it works. thanks
CREATE or REPLACE PROCEDURE av_sp_test(salary_var_1 in number)
AS
name_var avrajit.name%type;
salary_var avrajit.salary%type;
fail exception;
BEGIN
SELECT name,salary into name_var,salary_var from avrajit
WHERE salary=salary_var_1;
dbms_output.put_line('name is'||' '||name_var||' '||'salary is'||' '||salary_var);
EXCEPTION WHEN no_data_found THEN
dbms_output.put_line('OK');
END av_sp_test;
--------------------------------------------------
I have taken an input variable just to show diffrenet criterias.
begin
av_sp_test(1000);
end;
---------------------------------------------------
name is Sushil salary is 1000
Statement processed.
0.01 seconds
---------------------------------------------------
begin
av_sp_test(2000);
end;
---------------------------------------------------
OK
Statement processed.
0.00 seconds

Resources