Call procedure with nested table as parameter - oracle

How code procedure with nested table parameter? In table test I need insert data from loop eg. 1,2,3...
plsql
Declare
TYPE code_nt is table of varchar2(10);
l_codes code_nt := code_nt();
begin ​
​FOR i IN 1..APEX_APPLICATION.G_F01.COUNT LOOP
l_codes.extend;
l_codes(i) := to_char(i);
​END LOOP;
//here call procedure
//PKG_EMP.INSERT_EMP(PAR_1);
end;
package:
create or replace PACKAGE PKG_EMP AS
TYPE code_nt is table of varchar2(10);
l_codes code_nt := code_nt();
procedure INSERT_EMP (PAR_1 code_nt);
END;
create or replace PACKAGE BODY PKG_EMP AS
procedure INSERT_EMP (PAR_1 code_nt) AS
BEGIN
INSERT INTO test (ID) VALUES (value from code_nt);
END;
end;

Your code will not work as code_nt is a locally defined type in both your PL/SQL function and the PL/SQL anonymous block and despite having the same name and signature they are different data types.
You need to use the same data type in both:
Declare
l_codes PKG_EMP.code_nt := PKG_EMP.code_nt();
begin ​
FOR i IN 1..APEX_APPLICATION.G_F01.COUNT LOOP
l_codes.extend;
l_codes(i) := to_char(i);
-- or
-- l_codes(i) := TO_CHAR( APEX_APPLICATION.G_F01(i) );
​END LOOP;
PKG_EMP.INSERT_EMP(l_codes);
END;
/
You can declare your package as:
CREATE PACKAGE PKG_EMP AS
TYPE code_nt is table of varchar2(10);
PROCEDURE INSERT_EMP (PAR_1 code_nt);
END;
/
CREATE PACKAGE BODY PKG_EMP AS
procedure INSERT_EMP (PAR_1 code_nt) AS
BEGIN
FORALL i IN 1 .. par_1.COUNT
INSERT INTO test (ID) VALUES ( par_1(i) );
END;
END;
/
db<>fiddle here

Related

PLSQL variable doesn't get assigned by the given value

I am trying to assign a value from a table type variable to a varchar2 type table
CREATE OR REPLACE PACKAGE BODY TEST_SYS IS
TYPE line_table IS TABLE OF VARCHAR2(32767) INDEX BY BINARY_INTEGER;
PROCEDURE test(
val_ OUT VARCHAR2)
IS
temp_val VARCHAR2(4000);
svalues_ line_Table;
BEGIN
svalues_(1) := '! -$FILE_LINE=EXT01;2017-01;2017-
12;JJ01;;4021;;;;;;;;;;;;USD;;;;;;;;1000;1000;; -';
temp_val := svalues_(1);
val_ := temp_val;
END test;
END TEST_SYS
value assignment seems does not happen in the following statement. Does anybody has a clue on this?
temp_val := svalues_(1);
This example only for problem replicate, It may be not logically enhanced one. only to understand the problem.
Provided you have a package test_sys with code :
CREATE OR REPLACE PACKAGE TEST_SYS IS
PROCEDURE test( val_ OUT VARCHAR2);
END TEST_SYS;
and body of the package :
CREATE OR REPLACE PACKAGE BODY TEST_SYS IS
TYPE line_table IS TABLE OF VARCHAR2(32767) INDEX BY BINARY_INTEGER;
PROCEDURE test( val_ OUT VARCHAR2) IS
temp_val varchar2(4000);
svalues_ line_Table;
BEGIN
svalues_(1) := '! -$FILE_LINE=EXT01;2017-01;2017-12;JJ01;;4021;;;;;;;;;;;;USD;;;;;;;;1000;1000;; -';
temp_val := svalues_(1);
val_ := temp_val;
dbms_output.put_line(val_);
END test;
END TEST_SYS;
and call procedure test, you will be able to get the results as in the following :
SQL> set serveroutput on;
SQL> var str varchar2(4000);
SQL> exec test_sys.test(:str);
PL/SQL procedure successfully completed
str
---------
! -$FILE_LINE=EXT01;2017-01;2017-12;JJ01;;4021;;;;;;;;;;;;USD;;;;;;;;1000;1000;; -

Oracle : Procedure throws Invalid Datatype Error during execution ORA-00902: invalid datatype

CREATE TABLE T1 (EMP_NAME VARCHAR2 (40));
INSERT INTO t1
VALUES ('Vinoth');
COMMIT;
CREATE TABLE T2 (EMP_NAME VARCHAR2 (40));
CREATE OR REPLACE PACKAGE TEST_PKG_V
AS
PROCEDURE P_MAIN (p_status OUT VARCHAR2);
TYPE T1_TYPE IS RECORD (EMP_NAME T1.EMP_NAME%TYPE);
TYPE T1_TBL IS TABLE OF T1_TYPE;
END TEST_PKG_V;
/
CREATE OR REPLACE PACKAGE BODY TEST_PKG_V
AS
PROCEDURE P_MAIN (p_status OUT VARCHAR2)
IS
LV_T1_TBL T1_TBL := T1_TBL ();
CURSOR T1_CUR
IS
(SELECT EMP_NAME FROM t1);
BEGIN
OPEN T1_CUR;
LOOP
FETCH T1_CUR
BULK COLLECT INTO LV_T1_TBL
LIMIT 10000;
INSERT INTO t2 (EMP_NAME)
SELECT EMP_NAME FROM TABLE (LV_T1_TBL);
EXIT WHEN T1_CUR%NOTFOUND;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
p_status := 'FAIL';
RAISE;
END P_MAIN;
END TEST_PKG_V;
/
DECLARE
VAR VARCHAR2(4000);
BEGIN
TEST_PKG_V.P_MAIN(VAR);
END;
While executing the procedure throws the ORA-00902: invalid datatype.
If I comment out the insert statement inside the procedure, it is running perfectly fine. What is the problem here and help me with resolution.
For you to be able to do it like that, the types have to be created outside of your package. Here is the corrected version. Downside is, types beeing created, if you alter your table to change the type of the column, you might forget to modify the type.
CREATE TYPE T1_TYPE AS OBJECT ( EMP_NAME VARCHAR2(40));
CREATE TYPE T1_TBL AS TABLE OF T1_TYPE;
CREATE OR REPLACE PACKAGE TEST_PKG_V
AS
PROCEDURE P_MAIN (p_status OUT VARCHAR2);
END TEST_PKG_V;
/
CREATE OR REPLACE PACKAGE BODY TEST_PKG_V
AS
PROCEDURE P_MAIN (p_status OUT VARCHAR2)
IS
LV_T1_TBL T1_TBL;
CURSOR T1_CUR
IS
(SELECT T1_TYPE(EMP_NAME) EMP_NAME FROM t1);
BEGIN
OPEN T1_CUR;
LOOP
FETCH T1_CUR
BULK COLLECT INTO LV_T1_TBL
LIMIT 10000;
INSERT INTO t2 (EMP_NAME)
SELECT EMP_NAME FROM TABLE (LV_T1_TBL);
EXIT WHEN T1_CUR%NOTFOUND;
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS
THEN
p_status := 'FAIL';
RAISE;
END P_MAIN;
END TEST_PKG_V;
/
If you want your types to be generic and adapt to your table, I think you will have to do it something like that :
CREATE OR REPLACE PACKAGE TEST_PKG_V AS
TYPE T1_TYPE IS RECORD (EMP_NAME T1.EMP_NAME%TYPE);
TYPE T1_TBL IS TABLE OF T1_TYPE;
PROCEDURE P_MAIN (p_status OUT VARCHAR2);
FUNCTION GET_T1 RETURN T1_TBL PIPELINED;
END TEST_PKG_V;
/
CREATE OR REPLACE PACKAGE BODY TEST_PKG_V IS
CURSOR T1_CUR IS (SELECT EMP_NAME FROM t1);
FUNCTION GET_T1 RETURN T1_TBL PIPELINED IS
LV_T1_TBL T1_TBL;
BEGIN
OPEN T1_CUR;
FETCH T1_CUR BULK COLLECT INTO LV_T1_TBL;
CLOSE T1_CUR;
FOR IDX IN 1..LV_T1_TBL.COUNT LOOP
PIPE ROW (LV_T1_TBL(IDX));
END LOOP;
END;
PROCEDURE P_MAIN (p_status OUT VARCHAR2) IS
BEGIN
INSERT INTO t2 (EMP_NAME) SELECT EMP_NAME FROM TABLE (GET_T1);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
p_status := 'FAIL';
RAISE;
END P_MAIN;
END TEST_PKG_V;
/
alternatively, you could do it by iterating directly on your cursor :
CREATE OR REPLACE PACKAGE TEST_PKG_V AS
TYPE T1_TYPE IS RECORD (EMP_NAME T1.EMP_NAME%TYPE);
TYPE T1_TBL IS TABLE OF T1_TYPE;
PROCEDURE P_MAIN (p_status OUT VARCHAR2);
END TEST_PKG_V;
/
CREATE OR REPLACE PACKAGE BODY TEST_PKG_V IS
PROCEDURE P_MAIN (p_status OUT VARCHAR2) IS
CURSOR T1_CUR IS (SELECT EMP_NAME FROM t1);
BEGIN
FOR CURRENT_ROW IN T1_CUR LOOP
INSERT INTO t2 (EMP_NAME) VALUES (CURRENT_ROW.EMP_NAME);
END LOOP;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
p_status := 'FAIL';
RAISE;
END P_MAIN;
END TEST_PKG_V;
/

Procedure to update a table which is already populated

I want to create a procedure that adds new rows to an already existing table.But with the current procedure I've, I'm rewriting the entire table. The code for the current procedure is
CREATE TYPE t_tf_row AS OBJECT (
id NUMBER,
description VARCHAR2(50));
CREATE TYPE t_tf_tab IS TABLE OF t_tf_row;
create or replace procedure add_n_rows(
n_rows in number)
is
l_tab t_tf_tab := t_tf_tab();
begin
for i in l_tab.count .. l_tab.count + n_rows
loop
l_tab.extend;
l_tab(l_tab.last) := t_tf_row(i, 'Description for '|| i);
end loop;
end;
Here, everytime I'm rewriting the entire l_tab. I want to update the one which is already updated. Suggest me the right method for the required procedure.Thanks
This is because you're re-creating the object. You need to pass an instantiated version of the object into the procedure as a parameter:
create or replace procedure add_n_rows(
Pn_rows in number
, P_tab in out t_tf_tab ) is
begin
for i in P_tab.count .. P_tab.count + Pn_rows
loop
P_tab.extend;
P_tab(l_tab.last) := t_tf_row(i, 'Description for '|| i);
end loop;
end;
I've declared P_tab as an OUT parameter, this means you can alter it. If you don't want to do this then remove "out" and declare a local variable of the type t_tf_tab, which you can then alter.
You can then call it separately, for instance:
declare
l_tab t_tf_tab := t_tf_tab();
begin
l_tab.extend;
l_tab(l_tab.last) := t_tf_row(1. 'Hello');
add_n_rows(3, l_tab);
end;

Procedure to input number and output varchar2

I need to write a procedure to input let's say a rep_id and then output the rep_name that corresponds to the rep_id.
After that, I need to use another procedure to call the above procedure.
Here is what I have for the first procedure.
create or replace procedure p_inout
(v_rep_id in number)
As
v_first_name varchar2(20);
v_last_name varchar2(20);
begin
select first_name,last_name into v_first_name, v_last_name
from rep
where rep_id = v_rep_id;
dbms_output.put_line(v_first_name||' '||v_last_name);
end p_inout;
/
Execute p_inout(100);
And here is my procedure to call the above procedure
create or replace procedure p_call
is
v_first_name varchar2(20);
v_last_name varchar2(20);
begin
p_inout(100);
dbms_output.put_line(v_first_name||' '||v_last_name);
end p_call;
/
execute p_call
I was able to get the result but one guy told me that my call procedure should be like this
Create or replace procedure p_call
Is
V_name varchar2(20);
Begin
P_inout(100,v_name); --100 is a rep id
Dbms_output.Put_line(v_name);
End;
/
Execute p_call
Doesn't my procedure to call and his call procedure produce the same result?
CREATE TABLE minions (
rep_id DATE CONSTRAINT minions__rep_id__pk PRIMARY KEY,
rep_name NUMBER CONSTRAINT minions__rep_name__nn NOT NULL
CONSTRAINT minions__rep_name__u UNIQUE
)
/
CREATE OR REPLACE PROCEDURE myCatWasSick (
p_rep_id IN minions.rep_id%TYPE,
p_rep_name OUT minions.rep_name%TYPE
)
IS
BEGIN
SELECT rep_name
INTO p_rep_name
FROM minions
WHERE rep_id = p_rep_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
p_rep_name := NULL;
END myCatWasSick;
/
CREATE OR REPLACE PROCEDURE releaseTheBadgers
IS
the_badger NUMBER(10);
BEGIN
myCatWasSick( SYSDATE + 1, the_badger );
// Do something with the_badger.
END releaseTheBadgers;
/

Can't retrieve data using dynamic SQL from the function that returns the table

My types:
TYPE T_rowBalanceListForClient IS RECORD
(
RowCode Asset.RowCode%TYPE,
RowName Asset.RowName%TYPE
);
TYPE T_tableBalanceListForClient IS TABLE OF T_rowBalanceListForClient;
My function:
FUNCTION F_BalanceListForClient
(
p_ClientId Client.ClientId%TYPE
)
RETURN T_tableBalanceListForClient PIPELINED
AS
CURSOR CUR_TABLE IS
SELECT
RowCode,
RowName
FROM Asset;
BEGIN
FOR CUR_REC IN CUR_TABLE LOOP
PIPE ROW(CUR_REC);
END LOOP;
END;
Part of my stored procedure:
sql_statement := ' SELECT * FROM TABLE(:1)';
OPEN c_Result FOR sql_statement USING F_BalanceListForClient(11);
While building the package I reseive the Oracle error:
PLS-00457: expressions have to be of SQL types
In the common stored procedures calls like this are built and operate well (not dynamics):
PROCEDURE GET_BALANCE_STANDARD_LIST
(
c_Result OUT SYS_REFCURSOR,
p_ClientId Client.ClientId%TYPE DEFAULT NULL
)
AS
BEGIN
OPEN c_Result FOR
SELECT RowName FROM TABLE(F_BalanceListForClient(p_ClientId));
END;
Appreciate any help.
Thanks.
CREATE OR REPLACE TYPE T_rowBalanceListForClient IS OBJECT
(
RowCode NUMBER,
RowName VARCHAR2(200)
);
CREATE OR REPLACE TYPE T_tableBalanceListForClient AS TABLE OF T_rowBalanceListForClient;
/
CREATE OR REPLACE FUNCTION F_BalanceListForClient
(
p_ClientId NUMBER
)
RETURN T_tableBalanceListForClient PIPELINED
AS
CURSOR CUR_TABLE IS
SELECT
RowCode,
RowName
FROM Assets
; --put a filter of the p_clientId
BEGIN
FOR CUR_REC IN CUR_TABLE
LOOP
pipe row (T_rowBalanceListForClient (CUR_REC.RowCode, CUR_REC.RowName));
END LOOP;
RETURN;
END;
/
CREATE OR REPLACE PROCEDURE GET_BALANCE_STANDARD_LIST
(
c_Result OUT SYS_REFCURSOR,
p_ClientId NUMBER DEFAULT NULL
)
AS
sql_statement varchar2(200);
BEGIN
sql_statement := ' SELECT * FROM TABLE(F_BalanceListForClient(:1))';
OPEN c_Result FOR sql_statement USING p_ClientId;
END;
/
BEGIN
GET_BALANCE_STANDARD_LIST(:cur ,11);
END;
/

Resources