How to pass values in anonymous block with plsql table parameter - sql

Table abc has the following column
approved_ain
1
2
12
34
i have a procedure
create or replace procedure abc( p_admin varchar2,
p_approved_ain abc.approved_ain)--plsql table in parameter
begin
end;
now when i call this procedure in an anonymous block :-
declare
l_Admin varchar2(100);
l_approved_ain abc.approved_ain;
begin
abc(l_Admin ,l_approved_ain);
commit;
end;
How can i pass values of the approved_ain of plsql table to this anonymous block.? that is i want to test it by passing the values of abc table approved_ain column.......
Answer :
declare
l_Admin varchar2(100);
l_approved_ain abc.approved_ain;
begin
l_approved_ain(1) :=123;
l_approved_ain(2) :=4645;
abc(l_Admin ,l_approved_ain);
commit;
end;

Given the fact that you only want to test, what about just setting the values in the anonymous block:
declare
l_Admin varchar2(100) := 'string';
l_approved_ain abc.approved_ain := ???;
begin
abc(l_Admin ,l_approved_ain);
commit;
end;

Related

Can we call procedure inside function in oracle PL/SQL? If not then why?

In some places I heard that we cannot call procedure inside function in oracle PL/SQL. May I know why is it so?
Also why cant we call a procedure within a SELECT statement whereas we can call a function in the same SELECT statement.
Yes you can call a procedure from a function in Oracle PL/SQL.
You can't call a procedure from a SELECT statement because it doesn't return a value. A function can be called from a SELECT because it does:
select empno, calc_salary_function(empno) salary
from emp;
Calling a procedure from a SELECT makes no sense really:
select empno, fire_employee(empno) -- Will fail
from emp;
What would you expect to see in the second column of the results?
Procedure is not used to return a value. Procedures performs group of operations to achieve a task, where as function is used to return a value.
Procedure is executed as an operation, function can be used in the select statement derive a value
you can call procedure from function.
create table test(a varchar2(30));
create or replace procedure pro_insert_values
as
begin
insert into test values('anything');
end;
create or replace function fn_get_data
return number
as
begin
pro_insert_values;
return 1;
end;
sql>variable x number
exec :x:=fn_get_data
You can use below code to validate that procedure can be called from inside function.
create table country_name(id number, country varchar2(100));
insert all
into country_name values(1, 'INDIA')
into country_name values(2, 'UK')
SELECT * FROM DUAL;
/
CREATE OR REPLACE FUNCTION COUNTRY_FOUND_NOTFOUND(V_NAME VARCHAR2)
RETURN VARCHAR2
AS
V_COUNTRY VARCHAR2(100);
BEGIN
BEGIN
SELECT COUNTRY INTO V_COUNTRY FROM country_name WHERE COUNTRY = V_NAME;
RETURN 'FOUND';
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT_COUNTRY(V_NAME);
RETURN 'NOT FOUND';
END;
RETURN 'NOT FOUND';
END;
/
CREATE OR REPLACE PROCEDURE INSERT_COUNTRY(V_NAME IN VARCHAR2)
AS
V_ID NUMBER;
BEGIN
SELECT MAX(ID) INTO V_ID FROM COUNTRY_NAME;
INSERT INTO COUNTRY_NAME VALUES(V_ID+1, V_NAME);
END;
/
SET SERVEROUTPUT ON;
DECLARE
V_NAME VARCHAR2(100);
BEGIN
V_NAME := COUNTRY_FOUND_NOTFOUND('SPAIN');
DBMS_OUTPUT.PUT_LINE(V_NAME);
END;

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>

How to execute stored function in Oracle PL/SQL

I want to execute this stored function, and insert data in table t
I tried to find solution, but unsuccessfully
CREATE TABLE t (id number
, name varchar2(32)
, time date);
CREATE OR REPLACE PACKAGE t_api AS
FUNCTION func_ins (
p_row IN t%rowtype
) RETURN t.id%TYPE;
END t_api;
/
CREATE OR REPLACE PACKAGE BODY t_api AS
FUNCTION func_ins (
p_row IN t%rowtype
) RETURN t.id%TYPE
IS
l_id t.id%TYPE;
BEGIN
INSERT INTO t VALUES p_row RETURNING id INTO l_id;
RETURN l_id;
END func_ins;
END t_api;
/
declare
p_row t%rowtype;
begin
p_row.id := 1;
p_row.name := 'name';
p_row.time := sysdate;
t_api.func_ins(p_row);
end;
/
I got
PLS-00221
Thanks in advance
It works perfectly ,however i wouldn't recommend this design as its not a good practice to perform a DML within a function. Rather create a procedure instead of function and retrieve the Id using the out parameter.
Anonymous block to test the function when table is empty.You assign values for %ROWTYPE variable and insert.
declare
t_row t%rowtype;
x t.id%type;
begin
t_row.id := 2;
t_row.name := 'Test2';
t_row.time := sysdate;
x := t_api.func_ins(t_row);
dbms_output.put_line('x '||x);
end;
Output is
x 2
Modified code with Procedure to achieve the same result is below,
CREATE OR REPLACE PACKAGE t_api AS
FUNCTION func_ins (
p_row IN t%rowtype
) RETURN t.id%TYPE;
PROCEDURE proc_ins (
p_row IN t%rowtype,
l_id out t.id%TYPE
);
END t_api;
/
CREATE OR REPLACE PACKAGE BODY t_api AS
FUNCTION func_ins (
p_row IN t%rowtype
) RETURN t.id%TYPE
IS
l_id t.id%TYPE;
BEGIN
INSERT INTO t VALUES p_row RETURNING id INTO l_id;
RETURN l_id;
END func_ins;
PROCEDURE proc_ins (
p_row IN t%rowtype,
l_id out t.id%TYPE
)
IS
BEGIN
INSERT INTO t VALUES p_row RETURNING id INTO l_id;
END proc_ins;
END t_api;
/
Anonymous block to test the procedure,
declare
t_row t%rowtype;
x t.id%type;
begin
t_row.id := 3;
t_row.name := 'Test3';
t_row.time := sysdate;
t_api.proc_ins(t_row,x);
dbms_output.put_line('x '||x);
end;
Output is
x 3

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

Invalid Data Type error while casting PL/SQL Array to Table

I have a PL/SQL stored proc which needs to return a RefCursor type object as output parameter.
PROCEDURE usp_appnt_stts_driver_wraper2
( in_req_src_system_id IN NUMBER,
in_req_user_info IN VARCHAR2,
out_response_rec1 OUT SYS_REFCURSOR)
At the end of the SP, I am able to return Hard Coded values to my front end by using a Select Statement.
OPEN out_response_rec1 FOR
SELECT 'data1', 'data2', 'data3', 'data 4' FROM DUAL;
This works fine. But I need to send the data which I am getting from an Array.
The Array is populated like this,
FOR g_index IN g_slsnetoutbndarr.FIRST..g_slsnetoutbndarr.LAST
LOOP
out_response_rec.EXTEND;
IF g_SlsnetOutbndArr(g_index).Rectypdesc IS NOT NULL THEN
out_response_rec(g_index).Rectypdesc := g_SlsnetOutbndArr(g_index).Rectypdesc ;
out_response_rec(g_index).Recdetltcode := g_SlsnetOutbndArr(g_index).Recdetltcode;
out_response_rec(g_index).RecDetlDesc := g_SlsnetOutbndArr(g_index).RecDetlDesc ;
END IF;
END LOOP;
So at the end of this code, the Array Object out_response_rec has all the values I need.
But How do I transfer these values in the RefCursor output parameter?
Update 1
I have tried to create a new data type in the Package specification.
TYPE SlsnetOutbndRec IS RECORD(
Rectypdesc VARCHAR2(30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2(130));
TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec;
Finally I have tried to Cast the Array to table in my SP as
OPEN out_response_rec_result FOR
SELECT * FROM TABLE (Cast(out_response_rec AS SlsnetOutbndTabArr));
But this is throwing an Invalid Data Type error. The SP does not recognize the new data types I created.
So at the end of this code, the Array Object out_response_rec has all
the values I need.
But How do I transfer these values in the RefCursor output parameter?
As I could understand, you wanted to use a SYS_REFCUSOR to get the output of a Query plus the values that are in collection which is being populated by another Procedure. I have come up with a solution for your requirement see below inline explaination:
--Create a Object in Place of Record since we cannot use a PLSQL scope compnenet in SQL scope in Oracle 11g and below
CREATE OR REPLACE TYPE SlsnetOutbndRec IS OBJECT
(
Rectypdesc VARCHAR2 (30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2 (130)
);
--Create a table type of the Object
CREATE OR REPLACE TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec;
/
--Procedure
CREATE OR REPLACE PROCEDURE combined_rslt (var OUT SYS_REFCURSOR)
AS
v_var SlsnetOutbndTabArr := SlsnetOutbndTabArr ();
l_var SlsnetOutbndTabArr := SlsnetOutbndTabArr ();
BEGIN
--Populating the collection
v_var.EXTEND;
v_var (1) := SlsnetOutbndRec ('ABC', 1, 'A');
OPEN VAR FOR
SELECT 'CDE', 2, 'B' FROM DUAL
UNION ALL -- Combining the result of collection with the result of query
SELECT *
FROM TABLE (v_var) t;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
END;
Execution :
DECLARE
x SYS_REFCURSOR;
a VARCHAR2 (30);
b NUMBER;
c VARCHAR2 (130);
BEGIN
combined_rslt (var => x);
LOOP
FETCH x INTO a, b, c;
EXIT WHEN x%NOTFOUND;
DBMS_OUTPUT.put_line (a || ' ' || b || ' ' || c);
END LOOP;
END;
Results:
SQL> /
CDE 2 B
ABC 1 A
PL/SQL procedure successfully completed.
You need to create the types in the SQL scope (not the PL/SQL scope) if you want to use them in SQL statements (in versions prior to Oracle 12):
CREATE TYPE SlsnetOutbndRec IS OBJECT(
Rectypdesc VARCHAR2(30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2(130)
)
/
CREATE TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec
/
Then you can use it in your procedure (assuming your 3rd party data type is a collection or a VARRAY):
PROCEDURE usp_appnt_stts_driver_wraper2(
in_req_src_system_id IN NUMBER,
in_req_user_info IN VARCHAR2,
out_response_rec_result OUT SYS_REFCURSOR
)
IS
out_response_rec SlsnetOutbndTabArr := SlsnetOutbndTabArr();
g_slsnetoutbndarr ThirdPartyDataType := Get_From_3rd_party_Package();
BEGIN
FOR i IN 1 .. g_slsnetoutbndarr.COUNT LOOP
IF g_SlsnetOutbndArr(i).Rectypdesc IS NOT NULL THEN
out_response_rec.EXTEND;
out_response_rec := SlsnetOutbndRec(
g_SlsnetOutbndArr(i).Rectypdesc,
g_SlsnetOutbndArr(i).Recdetltcode,
g_SlsnetOutbndArr(i).RecDetlDesc
);
END IF;
END LOOP;
OPEN out_response_rec_result FOR
SELECT *
FROM TABLE( out_response_rec );
END;
If the 3rd party data type is an associative array then:
...
IS
out_response_rec SlsnetOutbndTabArr := SlsnetOutbndTabArr();
g_slsnetoutbndarr ThirdPartyDataType := Get_From_3rd_party_Package();
i ThirdPartyIndexType := g_slsnetoutbndarr.FIRST;
BEGIN
WHILE i IS NOT NULL LOOP
IF g_SlsnetOutbndArr(i).Rectypdesc IS NOT NULL THEN
out_response_rec.EXTEND;
out_response_rec := SlsnetOutbndRec(
g_SlsnetOutbndArr(i).Rectypdesc,
g_SlsnetOutbndArr(i).Recdetltcode,
g_SlsnetOutbndArr(i).RecDetlDesc
);
END IF;
i := g_slsnetoutbndarr.NEXT(i);
END LOOP;
...