How to get rid of compilation error in PL/SQL procedure? - sql

Being new to PL/SQL, I am unable to understand how to manage user-defined exceptions in procedures. My code is giving the Warning 'Procedure created with compilation errors'.
CREATE OR REPLACE PROCEDURE raise_salary
(eid IN employees.e_id%TYPE:=&emp_id,
raise IN employees.salary:=&salary_raise)
IS
cnt INTEGER;
salary employees.salary%TYPE;
BEGIN
SELECT count(*) INTO cnt FROM employees WHERE e_id=eid;
IF cnt=0 THEN
RAISE INVALID_ID;
ELSE
SELECT salary INTO sal FROM employees WHERE e_id=eid;
IF sal IS NULL THEN
RAISE NULL_VALUE;
ELSE
UPDATE employees SET salary=salary+raise WHERE e_id=eid;
dbms_output.put_line('Salary raised!');
END IF;
END IF;
EXCEPTION
WHEN INVALID_ID THEN
dbms_output.put_line('User ID does not exist!');
WHEN NULL_VALUE THEN
dbms_output.put_line('Salary is null in table!');
WHEN others THEN
dbms_output.put_line('Error!');
END;
/

An example:
SQL> CREATE OR REPLACE PROCEDURE testException
2 IS
3 BEGIN
4 raise INVALID_ID;
5 EXCEPTION
6 WHEN INVALID_ID THEN
7 dbms_output.put_line('Invalid ID');
8 END;
9 /
Warning: Procedure created with compilation errors.
A way to know the errors:
SQL> sho err
Errors for PROCEDURE TESTEXCEPTION:
LINE/COL ERROR
-------- -----------------------------------------------------------------
0/0 PL/SQL: Compilation unit analysis terminated
4/5 PL/SQL: Statement ignored
4/11 PLS-00201: identifier 'INVALID_ID' must be declared
6/10 PLS-00201: identifier 'INVALID_ID' must be declared
You need to declare the exceptions you use:
SQL> CREATE OR REPLACE PROCEDURE testException
2 IS
3 INVALID_ID exception;
4 BEGIN
5 raise INVALID_ID;
6 EXCEPTION
7 WHEN INVALID_ID THEN
8 dbms_output.put_line('Invalid ID');
9 END;
10 /
Procedure created.

In this DEMO you will see all 3 situation that this PROCEDURE should handle:
Here is the correct procedure:
CREATE OR REPLACE PROCEDURE raise_salary
(eid IN employees.e_id%TYPE,
raise IN employees.salary%type)
IS
cnt INTEGER;
sal employees.salary%TYPE;
INVALID_ID exception;
NULL_VALUE exception;
BEGIN
SELECT count(*)
INTO cnt
FROM employees
WHERE e_id=eid;
IF cnt=0 THEN
RAISE INVALID_ID;
ELSE
SELECT salary
INTO sal
FROM employees
WHERE e_id=eid;
IF sal IS NULL THEN
RAISE NULL_VALUE;
ELSE
UPDATE employees
SET salary = (SAL + raise)
WHERE e_id = eid;
dbms_output.put_line('Salary raised!');
END IF;
END IF;
exception
WHEN INVALID_ID THEN
dbms_output.put_line('User ID does not exist!');
WHEN NULL_VALUE THEN
dbms_output.put_line('Salary is null in table!');
WHEN others THEN
dbms_output.put_line('Error!');
END;
/
You had more than one errors and one was nicely explained by #Aleksej in his answer VOTE UP.
You also have a line:
SELECT salary INTO sal FROM employees WHERE e_id=eid;
But you have not declared sal.
Hope this helps...

Related

Implicit cursor indicates that it updates all rows in the table for a given identifier

I have a simple PL/SQL procedure that increases the salary of an employee in the EMP table of the SCOTT schema. This receives the employee number per parameter and increment. The UPDATE statement that performs the update does not filter by that identifier and when accessing the ROWCOUNT of the cursor indicates that all the rows in the table were updated.
If this update I do from SQL Plus. It only updates a row.
CREATE OR REPLACE PROCEDURE INCREASE_SALARY(
empno EMP.EMPNO%TYPE,
incre NUMBER
)
AUTHID DEFINER
AS PRAGMA AUTONOMOUS_TRANSACTION;
INCREMENT_MUST_BE_GREATER_THAN_ZERO EXCEPTION;
BEGIN
IF incre = 0 THEN
RAISE INCREMENT_MUST_BE_GREATER_THAN_ZERO;
END IF;
DBMS_OUTPUT.PUT_LINE('EMPLOYEE TO UPDATE: ' || empno);
UPDATE EMP
SET SAL = SAL + (SAL * incre / 100)
WHERE EMPNO = empno;
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SQL%ROWCOUNT)||' rows affected.');
IF SQL%ROWCOUNT > 0 THEN
INSERT INTO TABLA_LOG VALUES(USER, SYSDATE);
DBMS_OUTPUT.PUT_LINE('SALARY UPDATED SUCCESSFULLY');
ELSE
DBMS_OUTPUT.PUT_LINE('NO EMPLOYEE FOUND FOR THAT ID');
END IF;
COMMIT WORK;
EXCEPTION
WHEN INCREMENT_MUST_BE_GREATER_THAN_ZERO THEN
DBMS_OUTPUT.PUT_LINE('THE INCREMENT PERCENTAGE MUST BE GREATER THAN 0');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERROR : ' || SQLCODE || 'MENSAJE: ' || SQLERRM);
END;
/
When executing the procedure with the EMPNO 7900 (JAMES)
SET SERVEROUTPUT ON;
EXEC INCREASE_SALARY(7900, 20);
I get the following output:
EMPLOYEE TO UPDATE: 7900
13 rows affected.
SALARY UPDATED SUCCESSFULLY
Procedimiento PL/SQL terminado correctamente.
Does anyone know I'm doing wrong? Thanks in advance.
select count(1) from EMP WHERE EMPNO = 7900;
Return 1.
I'm using Oracle Enterprise 12c. The procedure compiles. The identifier error too long does not appear.
Your problem is empno = empno. These are interpreted as the column name.
You should try to give your arguments and local variables different names:
CREATE OR REPLACE PROCEDURE INCREASE_SALARY (
in_empno EMP.EMPNO%TYPE,
iin_ncre NUMBER
)
AUTHID DEFINER
AS PRAGMA AUTONOMOUS_TRANSACTION;
INCREMENT_MUST_BE_GREATER_THAN_ZERO EXCEPTION;
BEGIN
IF in_incre = 0 THEN
RAISE INCREMENT_MUST_BE_GREATER_THAN_ZERO;
END IF;
DBMS_OUTPUT.PUT_LINE('EMPLOYEE TO UPDATE: ' || in_empno);
UPDATE EMP
SET SAL = SAL + (SAL * in_incre / 100)
WHERE EMPNO = in_empno;
. . .

Function returned without value

This is the function which I designed. It got complied successfully
CREATE OR REPLACE FUNCTION "F_CHECK"
(
p_id IN VARCHAR2
p_name IN VARCHAR2)RETURN VARCHAR2
is
v_id VARCHAR2;
v_name VARCHAR2;
cnt pls_integer;
BEGIN
IF id IS NULL THEN
RETURN NULL;
END IF;
SELECT COUNT(*) INTO cnt from emp_new where id = p_id;
IF (cnt > 0) THEN
SELECT id, name INTO v_id, v_name from emp_new where id=p_id;
IF (v_id is null and p_id is null and v_name is null and p_name is null) THEN
return NULL;
ELSE
IF (v_name =trunc(p_name)) then
return NULL;
else
insert into employees values(p_id,p_name,sysdate);
end if;
end if;
end if;
exception
when DUP_VAL_ON_INDEX THEN
raise application error (-20001, 'NAME EXISTS UNDER DIFFERENT ID');
END F_CHECK;
But I'm not getting the expected result, when I execute the function
select F_CHECK(1,1) from dual;
ERROR I'M getting is:
SQL EEROR: ORA-06503: PL/SQL : FUNCTION RETURNED WITHOUT VALUE
You must return a value when the execution flow reaches (around line 22)
...
else
insert into employees values(p_id,p_name,sysdate);
end if;
...
In this case, the function does not return a value which is expected by the caller, hence the error.
You can instruct the PL/SQL compiler to warn you about such code (and other problems) with
ALTER SESSION SET PLSQL_WARNINGS='ENABLE:ALL'; prior to compilation.
One of the possible causes of exceptions that you will get when you run this code is: If your select into didn't return a value, you will get an exception, an nu-handled exception
Even though you have a return NULL in the end of the function, but still you need to catch all the exceptions that might occur
The area where you need to take care of is:
SELECT id, name INTO v_id, v_name from emp_new where id=p_id;
Surround it by Begin ... EXCEPTION WHEN NO_DATA_FOUND THEN ... END; block
Also, your insert statement might cause an exception if you violated some constraints on your table, so you might need to handle that also
Have a look at Error Handling in Oracle
Below is your updated code, also fixed code regarding the extra parenthesis
Edited
CREATE OR REPLACE FUNCTION F_CHECK
(
P_ID EMP_NEW.ID%TYPE,
P_NAME EMP_NEW.NAME%TYPE
)
RETURN VARCHAR2 IS
V_ID EMP_NEW.ID%TYPE;
V_NAME EMP_NEW.NAME%TYPE;
CNT NUMBER;
BEGIN
--IF ID IS NULL THEN
-- What is ID ?? is it P_ID
--Changed below
IF P_ID IS NULL THEN
RETURN 'Error: Add Value For Id';
END IF;
IF P_NAME IS NULL THEN
RETURN 'Error: Add Value For Name';
END IF;
SELECT
COUNT(*) INTO CNT FROM EMPLOYEES
WHERE ID = P_ID;
IF (CNT > 0) THEN
SELECT
ID, NAME INTO V_ID, V_NAME
FROM
EMP_NEW
WHERE
ID=P_ID;
----------------------------------------------------------------------------------------
--IF V_ID IS NULL AND P_ID IS NULL AND V_NAME IS NULL AND P_NAME IS NULL THEN
--The code above will always evaluate to False because P_ID at this stage is not null!
--Also, if P_Name must have a value, check it at the begining along with the ID, not here
----------------------------------------------------------------------------------------
IF V_ID IS NULL AND V_NAME IS NULL THEN
RETURN 'Error: Not found details';
ELSE
--Details are found
IF (V_NAME = TRUNC(P_NAME)) THEN
RETURN 'Name already assigned to this id';
ELSE --Its a new name, add it
INSERT INTO EMPLOYEES VALUES(P_ID,P_NAME,SYSDATE);
--Make sure your columns are only three and in the correct order as the Values specified
END IF;
END IF;
END IF;
RETURN 'Ok, added';
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
raise application error (-20001, 'NAME EXISTS UNDER DIFFERENT ID');
END F_CHECK;
A function MUST ALWAYS RETURN a VALUE of proper data type. Else, it would throw the following error:
ORA-06503: PL/SQL: Function returned without value
Cause: A call to PL/SQL function completed, but no RETURN statement was executed.
Action: Rewrite PL/SQL function, making sure that it always returns a value of a proper type.
Read ORA-06503: PL/SQL: Function returned without value
DB version : 11.2.0.2.0
Let’s see the various scenarios of this error :
Without a RETURN statement in the function body and without exception handler(most stupid way):
SQL> set serveroutput on
SQL> CREATE OR REPLACE FUNCTION f_test(i_val NUMBER)
2 RETURN NUMBER AS
3 o_val NUMBER;
4 BEGIN
5 SELECT 100 / i_val
6 INTO o_val
7 FROM DUAL;
8 END;
9 /
Function created
SQL> select f_test(100) from dual;
select f_test(100) from dual
ORA-06503: PL/SQL: Function returned without value
ORA-06512: at "F_TEST", line 8
Now, in the above code, the mathematical logic was correct, hence there was no SQL error to override the PL/SQL error. Let’s see how ORA-01476 will override the ORA-06503 error.
SQL> CREATE OR REPLACE FUNCTION f_test(i_val NUMBER)
2 RETURN NUMBER AS
3 o_val NUMBER;
4 BEGIN
5 SELECT 100 / i_val
6 INTO o_val
7 FROM DUAL;
8 END;
9 /
Function created
SQL> select f_test(0) from dual;
select f_test(0) from dual
ORA-01476: divisor is equal to zero
ORA-06512: at "F_TEST", line 5
Well, that’s quite obvious, isn’t it?
Without a RETURN statement in the exception handler(most common mistake):
SQL> CREATE OR REPLACE FUNCTION f_test(i_val NUMBER)
2 RETURN NUMBER AS
3 o_val NUMBER;
4 BEGIN
5 SELECT 100 / i_val
6 INTO o_val
7 FROM DUAL;
8
9 RETURN o_val;
10
11 EXCEPTION
12 WHEN OTHERS THEN
13 NULL;
14 END;
15 /
Function created
SQL> select f_test(0) from dual;
select f_test(0) from dual
ORA-06503: PL/SQL: Function returned without value
ORA-06512: at "F_TEST", line 14
Now let’s put a RETURN statement at required places and the code should work fine without any error:
SQL> CREATE OR REPLACE FUNCTION f_test(i_val NUMBER)
2 RETURN NUMBER AS
3 o_val NUMBER;
4 BEGIN
5 SELECT 100 / i_val
6 INTO o_val
7 FROM DUAL;
8
9 RETURN o_val;
10
11 EXCEPTION
12 WHEN OTHERS THEN
13 DBMS_OUTPUT.PUT_LINE('Came inside Exception handler');
14 RETURN 0;
15 END;
16 /
Function created
SQL> select f_test(0) from dual;
F_TEST(0)
0
Came inside Exception handler
Bottom line is that :
A function MUST ALWAYS RETURN a value of proper datatype, no matter from the body or exception.
We must do something with the error not just return junk. We must RAISE/log error and handle it, do something about the error so that underlying process has no impact.
Lastly, not to forget, EXCEPTION WHEN OTHERS THEN NULL; –> is itself a bug in the code waiting for its chance to break the code.

Oracle select query error inside procedure [duplicate]

This question already has answers here:
Oracle Procedure error (PLS-00428)
(5 answers)
Closed 8 years ago.
I'm getting error while I'm running select query inside procedure. Error print as follow:
[Error] PLS-00428 (24: 9): PLS-00428: an INTO clause is expected in this SELECT statement
CREATE OR REPLACE PACKAGE BODY PACK_EMP
IS
PROCEDURE find_employee(
P_ID NUMBER,
P_ERR OUT VARCHAR2
)
IS
BEGIN
IF P_ID IS NULL THEN
SELECT * FROM EMPLOYEE WHERE ID = P_ID
ELSE
P_ERR := 'An error occured on database!!';
END IF;
EXCEPTION
WHEN OTHERS THEN
P_ERR := SQLERRM;
END;
END;
CREATE OR REPLACE PACKAGE BODY PACK_EMP
IS
PROCEDURE find_employee(
P_ID NUMBER,
P_ERR OUT VARCHAR2,
V_VAL OUT VARCHAR
)
IS
BEGIN
IF P_ID IS NULL THEN
SELECT FIRTSNAME ||' '||LASTNAME INTO V_VAL FROM EMPLOYEE WHERE ID = P_ID
ELSE
P_ERR := 'An error occured on database!!';
END IF;
EXCEPTION
WHEN OTHERS THEN
P_ERR := SQLERRM;
END;
END;
Use a rowtype. Also I think you meant to say "IF P_ID IS NOT NULL THEN..." because if it is null then your select will never work.
CREATE OR REPLACE PACKAGE BODY PACK_EMP
IS
PROCEDURE find_employee(
P_ID NUMBER,
P_ERR OUT VARCHAR2
)
IS
empRec employee%rowtype; /* ADD THIS LINE */
BEGIN
IF P_ID IS NOT NULL THEN
SELECT * into empRec FROM EMPLOYEE WHERE ID = P_ID;
--now you can reference the columns like this: empRec.id
ELSE
P_ERR := 'An error occured on database!!';
END IF;
EXCEPTION
WHEN OTHERS THEN
P_ERR := SQLERRM;
END;
END;

ORACLE PL/SQL check whether string is NULL

Query 1:
create or replace procedure toUp(code in number)
is sname staff_master.staff_name%type;
recnotfound exception;
begin
select staff_name into sname from staff_master where staff_code=code;
if sname is NULL then
raise recnotfound;
else
update staff_master set staff_name=upper(staff_name) where staff_code=code;
end if;
exception
when recnotfound then dbms_output.put_line('Record not found');
end;
Query 2:
declare
commsn emp.comm%type;
no_comm exception;
begin
select comm into commsn from emp where empno=7369;
if commsn is NULL then
raise no_comm;
else
dbms_output.put_line('Comm is '||commsn);
end if;
exception
when no_comm then dbms_output.put_line('Commsn for emp doesnt exist');
end;
Here in Query 1 I'm checking whether sname is null.. However, when I pass an invalid code as a parameter to the procedure.. sname should be NULL and hence the exception 'recnotfound' must get raised.. but it is showing the following error:
SQL> exec toUp(7369);
BEGIN toUp(7369); END;
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at "LAB06TRG15.TOUP", line 6
ORA-06512: at line 1
But when I do the same with Query 2 it is working as expected..
I guess it has something to do with how varchar2 is checked for null.. Am I doing it correctly?
I modified the code as follows :
create or replace procedure toUp(code in number)
is
sname staff_master.staff_name%type;
recnotfound exception;
begin
select staff_name into sname from staff_master where staff_code=code;
if sname is NULL then
dbms_output.put_line('a');
raise recnotfound;
else
dbms_output.put_line('b');
--update staff_master set staff_name=upper(staff_name) where staff_code=code;
end if;
exception
when recnotfound then dbms_output.put_line('Record not found');
when no_data_found then raise recnotfound;
end;
I get :
BEGIN toUp(7369); END;
*
ERROR at line 1:
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at "LAB06TRG15.TOUP", line 16
ORA-01403: no data found
ORA-06512: at line 1
How do I solve this?
P.S. I want to do this using Exception only.. Its part of an assignment ..
If a query returns no rows then an "ORA-01403: no data found" error is raised. Your expectation, I think, is that execution will continue but no value will have been assigned to the variable -- that's not the case.
If what you want to do is check for the existence of a record then use:
select count(*)
into row_found
from ...
where ...
and rownum = 1;
this is guaranteed to return a single row with a value of 0 or 1 into the row_found variable.
With regard to your edit, you are not handling the raising of the user defined exception in the exception handling block. Wrap the SELECT with a BEGIN-END-EXCEPTION.
begin
begin
select ..
exception when NO_DATA_FOUND then raise recnotfound;
end;
if sname is NULL then
dbms_output.put_line('a');
raise recnotfound;
end if;
exception
when recnotfound then dbms_output.put_line('Record not found');
end;
I'm not clear what you're trying to do here though. Is the sname ever going to be returned as null from the query?
Actually, exceptions happens even before your IF statement. If SELECT INTO statement doesn't return a row, ORA-01403 is thrown. You might expect that in this situation NULL value is assigned to variable, but it is not so and exception is thrown instead.
You must add exception handling in your stored procedure to get over it. Documentation on how to do that can be found here
Sorry, don't have ORACLE now, so I can't check it, but it should be something like this:
...
select staff_name into sname from staff_master where staff_code=code;
exception
when NO_DATA_FOUND then ...handle no data...;
when TOO_MANY_ROWS then ...handle too many data rows...;
...
Exception will be raised as soon as no record is returned by your SELECT query.
The code will go into the exception there only and will not continue is that was your expectation.
Try this instead :
create or replace procedure toUp(code in number)
is sname staff_master.staff_name%type;err_count number;
recnotfound exception;
begin
select count(*) into err_count from staff_master where staff_code=code;
if count > 0 then
select staff_name into sname from staff_master where staff_code=code;
else
raise recnotfound;
Not sure whether the syntax is exactly correct, but I hope you get the drift
This is a sample procedure created regarding your query.
Please modify accordingly and try it should work. Thanks
create or replace procedure av_stack_test(sr_no_var in number)
as
nme avrajit.name%type;
no_rec exception;
num_count number;
begin
select count(*) into num_count from avrajit
where sr_no=sr_no_var;
if num_count>0 then
select name into nme from avrajit
where sr_no=sr_no_var;
update avrajit
set name=nme
where sr_no=sr_no_var;
else
raise no_rec;
end if;
dbms_output.put_line(sr_no_var);
exception
when no_rec then
dbms_output.put_line('No rec found');
when others then
dbms_output.put_line('Some other exception');
end;

No_data_found exception is propagating to outer block also?

In my code i am entering the salary which is not available in employees table and then again inserting duplicate employee_id in primary key column of employee table in exception block where i am handling no data found exception but i do not why No data found exception in the end also?
OUTPUT coming:
Enter some other sal
ORA-01400: cannot insert NULL into ("SCOTT"."EMPLOYEES"."LAST_NAME")
ORA-01403: no data found --This should not come according to logic
This is the code:
DECLARE
v_sal number:=&p_sal;
v_num number;
BEGIN
BEGIN
select salary INTO v_num from employees where salary=v_sal;
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE('Enter some other sal');
INSERT INTO employees (employee_id)values(100) ;
END;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm);
END;
The behaviour is that errors hurled in the EXCEPTIONS block get concatenated to SQLERRM, and hence propagated upwards. I grant you it is not documented but we can clearly see it here:
SQL> declare
2 v_sal t23.sal%type := 230;
3 l_num t23.sal%type;
4 begin
5 begin
6 begin
7 select sal into l_num
8 from t23 where sal = v_sal;
9 exception
10 when no_data_found then
11 dbms_output.put_line('inner exception::'||sqlerrm);
12 insert into t23 values (99, 'MR KNOX', v_sal);
13 end;
14 exception
15 when dup_val_on_index then
16 dbms_output.put_line('middle exception::'||sqlerrm);
17 insert into t23 (id, sal) values (99, v_sal);
18 end;
19 exception
20 when others then
21 dbms_output.put_line('outer exception::'||sqlerrm);
22 end;
23 /
inner exception::ORA-01403: no data found
middle exception::ORA-00001: unique constraint (APC.T23_PK) violated
ORA-01403: no data found
outer exception::ORA-01400: cannot insert NULL into ("APC"."T23"."LAST_NAME")
ORA-00001: unique constraint (APC.T23_PK) violated
ORA-01403: no data found
PL/SQL procedure successfully completed.
SQL>
Note: if there is a nested exception block which successfully handles the thrown exception it is not concatenated to SQLERRM. That is, the SQLERRM consists of a stack of unsucessfully handled exceptions.
In your exception block you try to insert into employees, but do not set the column last_name, which is not NULL-able.
ORA-01400: cannot insert NULL into ("SCOTT"."EMPLOYEES"."LAST_NAME")
The ORA-01403: no data found is part of the stack-trace, caused by your failed select.
You can either define DEFAULT values for all not-nullable columns or change your insert:
INSERT INTO employees (employee_id, last_name, ...) Values (100, 'Scott', ...);