I need to write an oracle procedure which will have an array of ID's as parameter.
Then I will return a cursor which contains result of select(1).
(1) - select * from table where id in(ID's)
As an option we can pass a string param and then convert string to array.
DECLARE
info sys_refcursor ;
error varchar(255);
BEGIN
package.test_function('1,2,3',info ,error);// info will contain a result cursor for select(1)
END;
Do you have other ideas?
You can create a user-defined collection type:
CREATE TYPE int8_list IS TABLE OF NUMBER(8,0);
Then your package:
CREATE PACKAGE pkg_name AS
PROCEDURE proc_name (
i_ids IN int8_list,
o_cursor OUT SYS_REFCURSOR
);
END;
/
CREATE PACKAGE BODY pkg_name AS
PROCEDURE proc_name (
i_ids IN int8_list,
o_cursor OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN o_cursor FOR
SELECT * FROM table_name WHERE id MEMBER OF i_ids;
END;
END;
/
Then you can call the procedure:
DECLARE
v_info sys_refcursor ;
v_id TABLE_NAME.ID%TYPE;
v_value TABLE_NAME.VALUE%TYPE;
BEGIN
pkg_name.proc_name(int8_list(1,2,3), v_info);
LOOP
FETCH v_info INTO v_id, v_value;
EXIT WHEN v_info%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_id || ' ' || v_value);
END LOOP;
END;
/
Which, for the sample data:
CREATE TABLE table_name (id, value) AS
SELECT LEVEL, CHR(64+LEVEL) FROM DUAL CONNECT BY LEVEL <= 5;
Outputs:
1 A
2 B
3 C
db<>fiddle here
I'm trying to return the number of rows per invoice_id using a function and procedure. Some invoice_id's have more than one row and I'm not sure how to fetch the count when I execute my procedure. As an example invoice_id(7) has just one row, but invoice_id(100) has four rows of information.
Create or replace function return_num_rows_function(invoice_id_text in varchar2)
Return varchar2
Is inv_id varchar2(20);
Begin
Select count(*)invoice_id into inv_id from invoice_line_items where invoice_id=invoice_id_text;
Return inv_id;
End;
Create or replace procedure return_num_rows (invoice_id_text in varchar2)
Is inv_id varchar(20);
line_item_desc invoice_line_items.line_item_description%type;
Begin
inv_id := return_num_rows_function(invoice_id_text);
If inv_id is not null then
Select count(*)invoice_id, line_item_description into inv_id,line_item_desc
From invoice_line_items where invoice_id = inv_id;
dbms_output.put_line('The number of rows returned:'|| inv_id);
dbms_output.put_line('Item description(s):'|| line_item_desc);
End if;
End;
set serveroutput on;
execute return_num_rows(7);
First of all do not use a string type variable for a numeric one
(invoice_id_text).
For your case it's better to use a procedure instead of called
function ( return_num_rows_function ), since you need two out
arguments returned.
A SQL Select statement cannot be used without Group By with aggegated and non-aggregated columns together ( i.e. don't use this one :
Select count(*) invoice_id, line_item_description
into inv_id,line_item_desc
From invoice_line_items
Where invoice_id = inv_id;
)
So, Try to create below procedures :
SQL> CREATE OR REPLACE Procedure
return_num_rows_proc(
i_invoice_id invoice_line_items.invoice_id%type,
inv_id out pls_integer,
line_item_desc out invoice_line_items.line_item_description%type
) Is
Begin
for c in
(
Select line_item_description
into line_item_desc
From invoice_line_items
Where invoice_id = i_invoice_id
)
loop
line_item_desc := line_item_desc||' '||c.line_item_description;
inv_id := nvl(inv_id,0) + 1;
end loop;
End;
/
SQL> CREATE OR REPLACE Procedure
return_num_rows(
i_invoice_id pls_integer
) Is
inv_id pls_integer;
line_item_desc invoice_line_items.line_item_description%type;
Begin
return_num_rows_proc(i_invoice_id,inv_id,line_item_desc);
If inv_id is not null then
dbms_output.put_line('The number of rows returned:' || inv_id);
dbms_output.put_line('Item description(s):' || line_item_desc);
End if;
End;
/
and call as in your case :
SQL> set serveroutput on;
SQL> execute return_num_rows(7);
Replace inv_id varchar2(20) with inv_id number;
and also if you want to get two outputs from procedure better to use refcursor.
I have problem with generate report to PDF via Report Server.
When generating a report show an error:
REP-8: A run time error in the PL/SQL development environment (DE)
PDE-PSD001 Could not resolve reference to while loading
REP-0008: An unexpected memory error occurred while initializing preferences.
The error occurs randomly. Once the report prints without problems and at a second printing report does not print and an error shown above,
although the report contains the same data.
My report use 3 Ref cursors and 3 select query. Use Report builder version 11.1.2.2.0.
Please, knows anyone where the problem is and how to resolve it?
Here is example, how I call Ref cusrcor:
Package:
TYPE t_info_rec IS RECORD (
column_1 VARCHAR2(15)
,column_2 NUMBER
,column_3 DATE
,column_4 VARCHAR2(15)
,column_5 VARCHAR2(15)
);
TYPE t_info_cur IS REF CURSOR RETURN t_info_rec;
PROCEDURE info(p_cur IN OUT t_info_cur
,p_parameter_1 NUMBER
,p_parameter_2 NUMBER
,p_parameter_3 VARCHAR2
,p_parameter_4 VARCHAR2
,p_parameter_5 NUMBER
,p_parameter_6 VARCHAR2);
Package Body:
PROCEDURE info(p_cur IN OUT t_info_cur
,p_parameter_1 NUMBER
,p_parameter_1 NUMBER
,p_parameter_1 VARCHAR2
,p_parameter_1 VARCHAR2
,p_parameter_1 NUMBER
,p_parameter_1 VARCHAR2)
IS
--
BEGIN
--
OPEN p_cur FOR
SELECT column_1,column_2,column_3,column_4,column_5
FROM table
;
--
EXCEPTION
WHEN OTHERS THEN
RAISE;
open p_cur for SELECT null,null,null,null,null FROM dual WHERE 1=0;
END info;
Call Ref cusrsor from Report Builders in RDF file:
function QR_2RefCurDS return package.t_info_cur is
c1 package.t_info_cur;
begin
package.info(c1
,p_parameter_1 => :P_PARAM1
,p_parameter_2 => :P_PARAM2
,p_parameter_3 => :P_PARAM3
,p_parameter_4 => :P_PARAM4
,p_parameter_5 => :P_PARAM5
,p_parameter_6 => :P_PARAM6
);
return c1;
end;
I am sorry for my English.
Thank you
Standa
There are few issue with your code. I can see that you declared type of ref cursor. Not sure if that can work. However without digging further in your code i show my code as below. In my code I am creating a record and passing the record type to procedure.
Solution 1
CREATE OR REPLACE PACKAGE Pkg_test
AS
TYPE t_info_rec IS RECORD
(
column_1 NUMBER,
column_2 NUMBER,
column_3 VARCHAR2 (15),
column_4 VARCHAR2 (15),
column_5 NUMBER
);
TYPE t_info_cur IS TABLE OF t_info_rec;
PROCEDURE info (p_cur IN OUT t_info_cur,
p_parameter_1 NUMBER,
p_parameter_2 NUMBER,
p_parameter_3 VARCHAR2,
p_parameter_4 VARCHAR2,
p_parameter_5 NUMBER,
p_parameter_6 VARCHAR2);
FUNCTION QR_2RefCurDS
RETURN t_info_cur;
END Pkg_test;
/
CREATE OR REPLACE PACKAGE BODY Pkg_test
as
PROCEDURE info(p_cur IN OUT t_info_cur
,p_parameter_1 NUMBER
,p_parameter_2 NUMBER
,p_parameter_3 VARCHAR2
,p_parameter_4 VARCHAR2
,p_parameter_5 NUMBER
,p_parameter_6 VARCHAR2)
IS
BEGIN
--OPEN p_cur FOR
SELECT p_parameter_1,p_parameter_2,p_parameter_3,p_parameter_4,p_parameter_5
bulk collect into p_cur
FROM dual ;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('In exception');
-- open p_cur for
SELECT null,null,null,null,null
bulk collect into p_cur
FROM dual WHERE 1=0;
End;
--Function Starting
FUNCTION QR_2RefCurDS
return t_info_cur
is
c1 t_info_cur;
begin
info(c1
,p_parameter_1 => 1 --:P_PARAM1
,p_parameter_2 => 2 --:P_PARAM2
,p_parameter_3 => 'Hello' --:P_PARAM3
,p_parameter_4 => 'Howru' --:P_PARAM4
,p_parameter_5 => 4 --:P_PARAM5
,p_parameter_6 => 'BYE' --:P_PARAM6
);
return c1;
end;
END Pkg_test;
/
Execution:
SQL> declare
outpt PKG_TEST.t_info_cur;
begin
outpt:=PKG_TEST.QR_2RefCurDS;
for i in 1..outpt.count
loop
dbms_output.put_line(outpt(i).column_1 ||' ' || outpt(i).column_2||' ' || outpt(i).column_3||' ' || outpt(i).column_4||' ' || outpt(i).column_5); end loop;
end;
/
1 2 Hello Howru 4
PL/SQL procedure successfully completed.
Solution 2
I use sys_refcursor to match your requirement:
CREATE OR REPLACE PACKAGE Pkg_test
AS
PROCEDURE info (p_cur IN OUT sys_refcursor,
p_parameter_1 NUMBER,
p_parameter_2 NUMBER,
p_parameter_3 VARCHAR2,
p_parameter_4 VARCHAR2,
p_parameter_5 NUMBER,
p_parameter_6 VARCHAR2);
FUNCTION QR_2RefCurDS
return sys_refcursor;
END Pkg_test;
/
CREATE OR REPLACE PACKAGE BODY Pkg_test
as
PROCEDURE info(p_cur IN OUT sys_refcursor
,p_parameter_1 NUMBER
,p_parameter_2 NUMBER
,p_parameter_3 VARCHAR2
,p_parameter_4 VARCHAR2
,p_parameter_5 NUMBER
,p_parameter_6 VARCHAR2)
IS
BEGIN
OPEN p_cur FOR
SELECT p_parameter_1,p_parameter_2,p_parameter_3,p_parameter_4,p_parameter_5
FROM dual ;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('In exception');
open p_cur for
SELECT null,null,null,null,null
--bulk collect into p_cur
FROM dual WHERE 1=0;
End;
--Function Starting
FUNCTION QR_2RefCurDS
return sys_refcursor
is
c1 sys_refcursor;
begin
info(c1
,p_parameter_1 => 1 --:P_PARAM1
,p_parameter_2 => 2 --:P_PARAM2
,p_parameter_3 => 'Hello' --:P_PARAM3
,p_parameter_4 => 'Howru' --:P_PARAM4
,p_parameter_5 => 4 --:P_PARAM5
,p_parameter_6 => 'BYE' --:P_PARAM6
);
return c1;
end;
END Pkg_test;
Execution
SQL> select PKG_TEST.QR_2RefCurDS from dual;
QR_2REFCURDS
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
:B5 :B4 :B3 :B2 :B1
---------- ---------- -------------------------------- -------------------------------- ----------
1 2 Hello Howru 4
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;
/
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;
/