Error in stored procedure execution in oracle - sql

create procedure proc_insert_salary(salary_emp in NUMBER,
empid in NUMBER(10),
desig in varchar2(20))
begin
DECLARE
gr_sal,hr,da,pf number;
BEGIN
set hr:= salary_emp*(15/100);
set da:= salary_emp*(8/100);
set pf := salary_emp*(35/100);
set gr_sal := salary_emp+hr+da-pf;
insert into emp_salary_details values (empid,desig, salary_emp, gr_sal);
end;
call proc_insert_salary (45000,10100,'C.E.O.')
when I call this procedure it gives error its in invalid state.

While #Guneli's answer is adequate, there's really no reason for the second block in this case. The following would be equivalent (and slightly simpler).
CREATE OR REPLACE PROCEDURE proc_insert_salary (salary_emp IN NUMBER,
empid IN NUMBER,
desig IN VARCHAR2) AS
hr NUMBER := salary_emp * (15 / 100);
da NUMBER := salary_emp * (8 / 100);
pf NUMBER := salary_emp * (35 / 100);
gr_sal NUMBER := salary_emp + hr + da - pf;
BEGIN
INSERT INTO emp_salary_details
VALUES (empid,
desig,
salary_emp,
gr_sal);
END;
/
Also, you should not that if you're going to have any other SQL in the same script (such as the call) then you need to end the procedure definition with a slash (/). This tells Oracle that procedure is finished and that it should compile it. Really, it's a good practice to always include it after procedural code.

Well, try this instead:
CREATE OR REPLACE PROCEDURE proc_insert_salary(
salary_emp IN NUMBER,
empid IN NUMBER,
desig IN VARCHAR2)
AS
BEGIN
DECLARE
gr_sal NUMBER;
hr NUMBER;
da NUMBER;
pf NUMBER;
BEGIN
hr := salary_emp*(15/100);
da := salary_emp*(8/100);
pf := salary_emp*(35/100);
gr_sal := salary_emp+hr+da-pf;
INSERT INTO emp_salary_details VALUES (empid,desig, salary_emp, gr_sal);
END;
END;
The things to note are that:
1) You can not show the size for the parameters of subprograms in PL/SQL, so NUMBER(10) or VARCHAR2(20) are incorrect.
2)The one line declaration of variables is not supported in PL/SQL, so instead of gr_sal,hr,da,pf number; you should use
gr_sal NUMBER;
hr NUMBER;
da NUMBER;
pf NUMBER;
3)To assign a value to variable you should not use SET, the ':=' is enough.
4)Also you have missed the 'end' clause for your second 'begin'.

Related

Getting invalid identifier ORA-00904

I have a procedure
Create or replace procedure schoolstudents(
LS1 OUT NUMBER,
LS2 OUT NUMBER,
val in VARCHAR2)
IS
val1 VARCHAR2(128);
schoolId NUMBER;
BEGIN
val11 := val;
schoolId := function(val1);
insert := 'Insert into sname values("a","b") RETURNING '||schoolId||' into :0, '||schoolId||' into :1';
Execute immediate insert USING OUT LS1,OUT LS2;
COMMIT;
I am getting an error
ORA-00933 : SQL command not properly ended.
You need an END; statement to complete the PL/SQL block.
You have not declared the insert variable and insert is a keyword and you should not use it as a variable name.
String literals need to be enclosed in single quotes (rather than double quotes).
The syntax for RETURNING INTO is RETURNING column1, column2 INTO :1, :2.
If you have the setup:
CREATE TABLE sname (
col1 VARCHAR2(20),
col2 VARCHAR2(20)
);
CREATE FUNCTION function( tablename IN VARCHAR2) RETURN NUMBER
IS
BEGIN
RETURN 1;
END;
/
Then your procedure would be:
Create or replace procedure schoolstudents(
LS1 OUT NUMBER,
LS2 OUT NUMBER,
tablename in VARCHAR2)
IS
lname VARCHAR2(128);
schoolId NUMBER;
insert_sql VARCHAR2(200);
BEGIN
lname := tablename;
schoolId := function(lname);
insert_sql := 'Insert into sname values(''a'',''b'') RETURNING '||schoolId||', '||schoolId||' into :1, :2';
EXECUTE IMMEDIATE insert_sql RETURNING INTO LS1, LS2;
COMMIT;
END;
/
However, you can rewrite your procedure without the dynamic SQl as:
Create or replace procedure schoolstudents(
LS1 OUT NUMBER,
LS2 OUT NUMBER,
tablename in VARCHAR2)
IS
schoolId NUMBER;
BEGIN
schoolId := function(tablename);
Insert into sname values('a','b');
LS1 := schoolId;
LS2 := schoolId;
COMMIT;
END;
/
db<>fiddle here
Quite a few errors. When fixed, it compiles (can't tell will it do what you planned).
you used insert variable, but never declared it. Anyway, you have to change its name, insert is for inserting
function(lname) is what, exactly? You can't name a function function (similarly to insert I mentioned previously). I have no idea what it is so I commented it out
don't use double, but single quotes for strings. However, in dynamic SQL, you have to escape them (by doubling them), or - simpler - use the q-quoting mechanism
Finally:
SQL> CREATE OR replace PROCEDURE schoolstudents(
2 ls1 OUT NUMBER,
3 ls2 OUT NUMBER,
4 tablename IN VARCHAR2
5 )IS
6 lname VARCHAR2(128);
7 schoolid NUMBER;
8 l_insert varchar2(200); --> not INSERT, it is reserved
9 BEGIN
10 lname := tablename;
11 schoolId := lname; --function(lname); --> what is FUNCTION?
12 l_insert := q'[Insert into sname values('a','b') --> q-mecanism; single, not double quotes
13 RETURNING ]'||schoolId||' into :0, '||schoolId||' into :1';
14 Execute immediate l_insert
15 USING OUT ls1, OUT ls2;
16 COMMIT;
17 END;
18 /
Procedure created.
SQL>
As others have pointed out there are quite a few problems with this code. What no one pointed out is that there doesn't appear to be any reason to use an EXECUTE IMMEDIATE here. I suggest:
Create or replace procedure schoolstudents(LS1 OUT NUMBER,
LS2 OUT NUMBER,
tablename in VARCHAR2)
IS
schoolId NUMBER;
BEGIN
schoolId := some_function(tablename);
Insert into sname values('a', 'b');
COMMIT;
LS1 := schoolId;
LS2 := schoolID;
END schoolstudents;

How to write a PL/SQL procedure with an Instead Of Trigger as Dynamic SQL?

I wrote a PL/SQL function and am using dynamic SQL to execute a Create trigger statement as follows :-
CREATE OR REPLACE FUNCTION register_driver1(driver_name IN VARCHAR, pass_word IN VARCHAR) RETURN NUMBER AS
sql_stmt VARCHAR2(500);
driver_id NUMBER;
new_view_name VARCHAR(50); --<-----Line 4
BEGIN
--function statements
sql_stmt := 'CREATE OR REPLACE TRIGGER reg_vehicle
INSTEAD OF INSERT ON '||new_view_name||
' FOR EACH ROW
DECLARE
vehicle_id NUMBER;
BEGIN
vehicle_id := vehicle_ids.nextval
INSERT INTO Vehicles VALUES(:NEW.Model, :NEW.Seats, :NEW.reg_no, vehicle_id, '||driver_id||');
END;';
EXECUTE IMMEDIATE sql_stmt; --<-----Line 32
--Remaining function body
END;
/
Here, the variables new_view_name, driver_id are defined above this code snippet. Vehicle is a table(Model, Seats, Reg_no, vehicel_id, driver_id) and reg_vehicle(Model, Seats, Reg_no) is a view having Vehicles of a particular driver_id.
vehicle_ids is a sequence created outside the procedure.
The above shows compilation error at the EXECUTE IMMEDIATE line. What is the correct way to do this?
The error shown when function is called with some driver_name and pass_word:-
ORA-24344: success with compilation error ORA-06512: at "ADMIN.REGISTER_DRIVER1", line 32 ORA-06512: at line 4
Well, I wouldn't recommend creating objects dynamically.
If you - for some reason - insist on that, then another problem: you can't perform DDL here as you'd call a function as e.g.
select register_driver1(...) from dual;
and get
ORA-14552: cannot perform a DDL, commit or rollback inside a query or DML
So - switch to a procedure with an OUT parameter to return whatever that function is supposed to return.
As of error you got: you miss BEGIN. Something like this compiles:
SQL> CREATE OR REPLACE PROCEDURE register_driver1 (driver_name IN VARCHAR,
2 pass_word IN VARCHAR)
3 AS
4 sql_stmt VARCHAR2 (500);
5 driver_id NUMBER;
6 new_view_name VARCHAR (50);
7 BEGIN
8 sql_stmt :=
9 'CREATE OR REPLACE TRIGGER reg_vehicle
10 INSTEAD OF INSERT ON '
11 || new_view_name
12 || ' FOR EACH ROW
13 DECLARE
14 vehicle_id NUMBER;
15 BEGIN
16 vehicle_id := vehicle_ids.nextval
17 INSERT INTO Vehicles VALUES(:NEW.Model, :NEW.Seats, :NEW.reg_no, vehicle_id, '
18 || driver_id
19 || ');
20 END;';
21
22 -- EXECUTE IMMEDIATE sql_stmt;
23 DBMS_OUTPUT.put_line (sql_stmt);
24 END;
25 /
Procedure created.
SQL>
SQL> SET SERVEROUTPUT ON;
SQL> EXEC register_driver1('a', 'b');
CREATE OR REPLACE TRIGGER reg_vehicle
INSTEAD OF INSERT ON FOR
EACH ROW
DECLARE
vehicle_id NUMBER;
BEGIN
vehicle_id := vehicle_ids.nextval
INSERT
INTO Vehicles VALUES(:NEW.Model, :NEW.Seats, :NEW.reg_no, vehicle_id, );
END;
PL/SQL procedure successfully completed.
SQL>

Insert values with PL/SQL Tables and Record

I have done a procedure in a package to enter data into a table using a PL / SQL table.
I have put some data in the code to execute from the package and verify that the procedure works, but I don't know how to execute it from the worksheet with the data that i enter.
CREATE TABLE TB_CRUD_MAC
( "K_CODIGO" NUMBER(10,0),
"A_NUMNIT" VARCHAR2(11 BYTE),
"N_NOMBRE" VARCHAR2(11 BYTE),
"N_APELLI" VARCHAR2(11 BYTE),
"F_FECHA" DATE,
"I_ESTADO" VARCHAR2(1 BYTE),
"K_CLASIF" VARCHAR2(1 BYTE)
)
create or replace PACKAGE PK_CRUD_MAC AS
TYPE R_REGISTRO IS RECORD (
codigo TB_CRUD_MAC.K_CODIGO%TYPE := K_CODIGO.nextval,
numnit TB_CRUD_MAC.A_NUMNIT%TYPE,
nombre TB_CRUD_MAC.N_NOMBRE%TYPE,
apelli TB_CRUD_MAC.N_APELLI%TYPE,
fecha TB_CRUD_MAC.F_FECHA%TYPE,
estado TB_CRUD_MAC.I_ESTADO%TYPE,
clasif TB_CRUD_MAC.K_CLASIF%TYPE
);
TYPE T_REGISTRO IS TABLE OF R_REGISTRO;
PROCEDURE PR_INSERT_LISTA (PT_REGISTRO IN OUT T_REGISTRO);
END;
create or replace PACKAGE BODY PK_CRUD_MAC AS
PROCEDURE PR_INSERT_LISTA (PT_REGISTRO IN OUT T_REGISTRO)
IS
BEGIN
PT_REGISTRO := T_REGISTRO();
PT_REGISTRO.extend(3);
PT_REGISTRO(1).nombre := 'Andres';
PT_REGISTRO(2).nombre := 'Martinez';
INSERT INTO TB_CRUD_MAC VALUES (K_CODIGO.nextval, '123', PT_REGISTRO(1).nombre, 'AAA', '28/03/00', '1', '1');
INSERT INTO TB_CRUD_MAC VALUES (K_CODIGO.nextval, '123', PT_REGISTRO(2).nombre, 'AAA', '28/03/00', '1', '1');
END;
END;
Edit:
I try this but dont work:
DECLARE
reg PK_CRUD_MAC.T_REGISTRO := PK_CRUD_MAC.T_REGISTRO();
BEGIN
reg().extend;
reg(1).nombre := 'AAA';
PK_CRUD_MAC.PR_INSERT_LISTA(reg);
END;
Error: PLS-00355: use of pl/sql table not allowed in this context
a cursor like you had in mind, does not work. try this:
DECLARE
reg PK_CRUD_MAC.T_REGISTRO := PK_CRUD_MAC.T_REGISTRO();
BEGIN
reg.extend;
reg(1).numnit := 'Aaa';
reg(1).nombre := 'aaa';
PK_CRUD_MAC.PR_INSERT_LISTA(reg);
END;
/
Well running your procedure is simply a matter of declaring a variable and calling the procedure, say in an anonymous PL/SQL block:
declare
l_table pk_crud_mac.t_registro;
begin
pk_crud_mac.pr_insert_lista(l_table);
end;
/
However, this doesn't get us very far. Probably you don't want to have those values hard-coded in the procedure. Instead you should populate the values in the calling program and pass them to the procedure.
Oh, and defaulting R_REGISTRO.codigo to a sequence value may seem like a neat idea but it can cause problems with initialisation. It's probably better to handle K_CODIGO.nextval in the internals of the code.
So, re-writing your package like this ...
create or replace package pk_crud_mac as
type r_registro is record (
codigo tb_crud_mac.k_codigo%type,
numnit tb_crud_mac.a_numnit%type,
nombre tb_crud_mac.n_nombre%type,
apelli tb_crud_mac.n_apelli%type,
fecha tb_crud_mac.f_fecha%type,
estado tb_crud_mac.i_estado%type,
clasif tb_crud_mac.k_clasif%type
);
type t_registro is table of r_registro;
procedure pr_insert_lista (pt_registro in out t_registro);
end;
/
create or replace package body pk_crud_mac as
procedure pr_insert_lista (pt_registro in out t_registro)
is
begin
for idx in pt_registro.first()..pt_registro.last loop
pt_registro(idx).codigo := k_codigo.nextval;
end loop;
forall idx in pt_registro.first()..pt_registro.last
insert into tb_crud_mac values pt_registro(idx);
end;
end;
/
...means we call it like this:
declare
l_record pk_crud_mac.r_registro;
l_table pk_crud_mac.t_registro := new pk_crud_mac.t_registro();
begin
l_record.nombre := 'Andres';
l_record.numnit := '123';
l_record.apelli := 'AAA';
l_record.fecha := date '2000-03-28';
l_record.estado := '1';
l_record.clasif := '1';
l_table.extend();
l_table(l_table.count()) := l_record;
l_record.nombre := 'Martinez';
l_table.extend();
l_table(l_table.count()) := l_record;
pk_crud_mac.pr_insert_lista(l_table);
for idx in 1..l_table.count() loop
dbms_output.put_line(l_table(idx).codigo ||':' || l_table(idx).nombre);
end loop;
end;
/
If you enable SERVEROUTPUT the call to dbms_output.put_line will show you that the sequence value has been assigned to each record.

Anonymous PL/SQL Block to call a procedure with collection

I need to test a package procedure which accepts a PL/SQL table and returns SYS_REFCURSOR.
What I am looking for is Anonymous PL/SQL block which does the following :
populates PL/SQL table and pass to procedure
The relevant code is below:
create or replace type rec_typ as object
(
fname varchar2(10),
lname varchar2(10)
)
/
create or replace type rec_arr as table of rec_typ
/
create or replace package get_emp_pkg
as
procedure get_emp
(
l_rec rec_arr,
p_out out sys_refcursor
);
end;
/
create or replace package body get_emp_pkg
as
procedure get_emp
(
l_rec rec_arr,
p_out out sys_refcursor
)
as
l_out rec_arr;
begin
open p_out for select * from table ( l_out );
end;
end;
/
You need to declare and populate a table collection, and declare a cursor variable; and then pass those into the procedure call; e.g.:
declare
l_rec rec_arr;
l_cur sys_refcursor;
begin
l_rec := rec_arr(rec_typ('Joe','Bloggs'), rec_typ('Mary','Berry'));
get_emp_pkg.get_emp(l_rec, l_cur);
-- do something with the cursor
close l_cur;
end;
/
If you are just testing it and you're using SQL*Plus or SQL Developer you could simplify this to use a client bind variable:
variable cur refcursor;
declare
l_rec rec_arr;
begin
l_rec := rec_arr(rec_typ('Joe','Bloggs'), rec_typ('Mary','Berry'));
get_emp_pkg.get_emp(l_rec, :cur);
end;
/
print cur;
or even more simply:
variable cur refcursor;
begin
get_emp_pkg.get_emp(rec_arr(rec_typ('Joe','Bloggs'), rec_typ('Mary','Berry')), :cur);
end;
/
print cur;
and you could even replace that with an execute call, which is just a wrapper for an anonymous block anyway, so functionally almost identical.
Your procedure has a mistake though; you're declaring a local l_out variable and then opening the ref cursor based on that, but you don't ever populate it - so the ref cursor result set will always be empty. If you change it to use l_rec instead (though I would call that p_rec, with the p_ prefix indicating a parameters and leave the l_ prefix for local variables):
create or replace package body get_emp_pkg
as
procedure get_emp
(
l_rec rec_arr,
p_out out sys_refcursor
)
as
begin
open p_out for select * from table ( l_rec );
end get_emp;
end;
/
then the bind variable versions above both see:
PL/SQL procedure successfully completed.
FNAME LNAME
---------- ----------
Joe Bloggs
Mary Berry

plsql function must be declared

So I have this function called cal_approv that calculates a quantity of supply, by selecting
the sum of ordered quantity, the "present" quantity, and the min quantity of a giving product(idart)...fecthing information into cursor then inserting it into a result table(resultat), when i try to run this code
accept idarticle prompt 'Donner lid article : '
declare
FUNCTION cal_approv(code_art article.idart%type)
RETURN number IS qte _app number;
qtot number;
qtestk number;
qtemin number;
begin
select sum(qte_com) into qtot from lig_com where idart=code_art;
select qtestk into qtestk from article where idart=code_art;
select qtemin into qtemin from article where idart=code_art;
if ((qtot/qtestk)>(2*qtemin)) then
qte_app := qtot-qtestk*1.5;
else
qte_app := qtot*1.2;
end if;
return qte_app;
end;
/
drop function cal_approv;
declare
cursor arts is select idart, desart,pu from article where qtestk <= qtemin;
ida number;
da number;
qa number;
pa number;
begin
open arts;
loop
fetch arts into ida, da, pa;
qa := cal_ap prov(&idarticle);
pa := pa*cal_approv(&idarticle);
exit when(arts%notfound);
insert into resultat values(ida,da, qa,pa);
end loop;
close arts;
end;
/
select * from resultat;
I get this error
PLS-00201: identifier 'CAL_APPROV' must be declared
any help is appreciated!
Here's what I get when I indent your first block of PL/SQL code:
declare
FUNCTION cal_approv(code_art article.idart%type)
RETURN number
IS
qte_app number;
qtot number;
qtestk number;
qtemin number;
begin
select sum(qte_com) into qtot from lig_com where idart=code_art;
select qtestk into qtestk from article where idart=code_art;
select qtemin into qtemin from article where idart=code_art;
if ((qtot/qtestk)>(2*qtemin)) then
qte_app := qtot-qtestk*1.5;
else
qte_app := qtot*1.2;
end if;
return qte_app;
end;
/
Note that the end in the second-last line ends the function, not the anonymous PL/SQL block. This will cause an error because Oracle is expecting something like the following:
declare
FUNCTION cal_approv(code_art article.idart%type)
RETURN number
IS
-- ...
begin
-- body of function omitted
end;
begin
-- do something with this function
end;
/
Also, to be clear, a function declared within a PL/SQL block, rather than with a CREATE OR REPLACE FUNCTION ... statement, exists only while that block is being executed. Once the block completes, the function no longer exists. So you can write the following block:
DECLARE
FUNCTION add_one(p_in IN NUMBER)
RETURN NUMBER IS
BEGIN
RETURN p_in + 1;
END;
BEGIN
dbms_output.put_line(TO_CHAR(add_one(4));
END;
/
This should write 5 to dbms_output, if that has been enabled.
On the other hand, this won't work:
DECLARE
FUNCTION add_one(p_in IN NUMBER)
RETURN NUMBER IS
BEGIN
RETURN p_in + 1;
END;
BEGIN
NULL;
END;
/
BEGIN
dbms_output.put_line(TO_CHAR(add_one(4));
END;
/
This doesn't work because the function add_one is no longer available when the first block completes. The second block will fail with an error because it cannot find the function add_one.
There is no point attempting to use DROP FUNCTION with your function. DROP FUNCTION can only drop stored functions, and because your function is declared within a PL/SQL block, it is not a stored function.
As I see it you have two alternatives:
declare the function as a stored function, using CREATE OR REPLACE FUNCTION ..., outside of any DECLARE block, or
move all of the code that uses the function into the same block that declares the function.
In the latter case, your code would look something like the following (abbreviated for clarity):
declare
FUNCTION cal_approv(code_art article.idart%type)
RETURN number
IS
-- ...
begin
-- ...
end;
cursor arts is select idart, desart,pu from article where qtestk <= qtemin;
ida number;
da number;
qa number;
pa number;
begin
open arts;
loop
-- ...
end loop;
close arts;
end;
/