PL/SQL OUT SYS_REFCURSOR on associative array - sql

I have an associative array inside a procedure:
create or replace PROCEDURE RET_STRING_TAB(o_cursor OUT SYS_REFCURSOR) IS
type type_ntz is record(type_id number, type_uname varchar(32), type_pwd varchar(16));
type ntz_array is table of type_ntz index by pls_Integer;
v_ntz_array ntz_array;
BEGIN
Select id,uname,pwd BULK COLLECT INTO v_ntz_array FROM users;
As you can see i want to out a refcusor on that array, to work further in Java with it. I tried doing:
OPEN o_cursor FOR
SELECT * FROM TABLE(v_ntz_array);
But it keeps saying:
PL/SQL: SQL Statement ignored
PL/SQL: cannot access rows from a non-nested table item
PLS: expression is of wrong type
Thanks in advance for any help!

You TYPE needs to be create out of the procedure, so your calling program (ie: java) can access it and know it's composition:
CREATE OR REPLACE TYPE type_ntz IS OBJECT(type_id number, type_uname varchar(32), type_pwd varchar(16));
/
then:
create or replace PROCEDURE RET_STRING_TAB(o_cursor OUT SYS_REFCURSOR) IS [...]
UPDATE
I've re-read your question and for me the easiest way to do what you are looking for is:
create or replace PROCEDURE RET_STRING_TAB(o_cursor OUT SYS_REFCURSOR) IS
BEGIN
OPEN o_cursor FOR
Select id,uname,pwd FROM users;
END;
/

Related

Store data of each iteration of a LOOP and return all of them on a ref cursor?

Sorry for the bad question name, i'll give you an example to be more specific.
var1 VARCHAR(20);
var2 VARCHAR(20);
--MYRECORD CONTAINS COLUMNS ELEMENT0, VAL
FOR MYRECORD IN EXPLICITCURSOR LOOP
SELECT COL1, COL2 INTO var1, var2 FROM table1 WHERE table1.COLUMNT=MYRECORD.VAL;
END LOOP;
As you can see i have a LOOP and inside it i have a SELECT. By now, for testing, i'm saving the results into two variables overwritten everytime.
I need to save on each iteration (ELEMENT0, COL1, COL2) and i'd give them on output with a REF CURSOR.
EDIT1: I'm looking in this moment on the possibility to define a RECORD and a TABLE of my record type. Can anyone give me an example for my case? I'm having problems on setting a table as output parameter.
This is what i have prepared at the beginning of the package.
TYPE my_record is RECORD(
ELEMENT0 varchar2(20),
COL1 varchar2(20),
COL2 varchar2(20));
TYPE my_table IS TABLE OF my_record;
and for now i'm using an OUT parameter for my procedure like this:
TABLERESULT OUT my_table
I'm trying to insert my three varchar values inside my OUT param on each iteration of LOOP in this way (values are setted correctly):
INSERT INTO TABLERESULT(ELEMENT0,COL1,COL2) VALUES(ELEMENT0,COL1,COL2);
and it gives me error:
PL/SQL: ORA-00942: table or view does not exist
I'm doing something wrong using this type of OUT param?
Any suggestions? Thank you. (I'm using Oracle 11g)
EDIT2: By the help of #APC i found that naming error and now the compiler doesn't give problems. I'll continue and i'll let you know.
Here this can be achieved simply without any iterations required.
Hope below snippet helps. Please pardon any syntax error as i dont have any workspace to execute this command.
--Wwhat i would suggest is rather going row-by-row this can be achieved by single iteration and can be returnedas ref cursor as output.
DECLARE
lv sys.odcivarchar2list;
lv_ref sys_refcursor;
BEGIN
SELECT val BULK COLLECT
INTO lv
FROM TABLE2;
OPEN lv_ref FOR SELECT * FROM TABLE1 WHERE TABLE1.COL IN
(SELECT COLUMN_VALUE FROM TABLE(lv)
);
END;
/
Create an object type that matches your record structure.
Create a nested table type of those object types.
For each tuplet of strings, add to the array.
Then return a cursor variable as follows:
OPEN cv FOR SELECT FROM TABLE (my_array);
RETURN cv;
The table function converts the array into a result set that can be assigned to the cursor variable.
Here's a link to a LiveSQL script that will run the code you see below:
https://livesql.oracle.com/apex/livesql/file/content_FFTOKNC4AHGPOQE79FF76S7EQ.html
CREATE OR REPLACE TYPE three_ot
AUTHID DEFINER IS OBJECT
(
element0 VARCHAR2 (200),
col1 VARCHAR2 (200),
col2 VARCHAR2 (200)
)
/
CREATE OR REPLACE TYPE three_nt IS TABLE OF three_ot
/
CREATE OR REPLACE FUNCTION data_for_you
RETURN SYS_REFCURSOR
AUTHID DEFINER
IS
l_cursor SYS_REFCURSOR;
l_nt three_nt;
BEGIN
SELECT three_ot (TO_CHAR (employee_id), last_name, first_name)
BULK COLLECT INTO l_nt
FROM hr.employees;
OPEN l_cursor FOR SELECT * FROM TABLE (l_nt);
RETURN l_cursor;
END;
/
DECLARE
l_cursor SYS_REFCURSOR := data_for_you ();
l_three three_ot;
element0 VARCHAR2 (200);
col1 VARCHAR2 (200);
col2 VARCHAR2 (200);
BEGIN
LOOP
FETCH l_cursor INTO element0, col1, col2;
EXIT WHEN l_cursor%NOTFOUND;
DBMS_OUTPUT.put_line (col1);
END LOOP;
CLOSE l_cursor;
END;
/

Invalid identifier error while running plsql block

Since my school does not allow me to post the code, hence i had to come back home and put up an example to show the issue i am facing. My school asked me to do a homework on dynamic sql to create a table and later insert one dummy record to it. But while doing it I am facing the below issue. Please find the code below for your reference. Thank you.
Procedure:
create or replace procedure table_creation(table_name in varchar2,col1 varchar2,col2 varchar2)
is
l_stat varchar2(3000);
v_stat varchar2(1000);
a varchar2(10):='1';
b varchar2(10):='Dummy';
begin
l_stat:='create table '||table_name||' ("'||col1||'" varchar2(10),"'||col2||'" varchar2(10))';
execute immediate l_stat;
execute immediate 'insert into '||table_name||'('||col1||','||col2||') values (:x,:y)' using a,b;
end;
/
Plsql Block to call the procedure:
set serveroutput on;
declare
a varchar2(10);
b varchar2(10);
c varchar2(10);
begin
a:='Example';
b:='id';
c:='nm';
table_creation(a,b,c);
dbms_output.put_line('Yes');
end;
/
The procedure is getting created perfectly and while running the above block i am getting the below error .
declare
*
ERROR at line 1:
ORA-00904: "NM": invalid identifier
ORA-06512: at "SYS.TABLE_CREATION", line 9
ORA-06512: at line 9
But when I checked the created table. The table exists with 0 records. The structure of the table is as follows.
Name Null? Type
----------------------------------------- -------- ---------------
ID VARCHAR2(10)
NM VARCHAR2(10)
Any help from your end is much appreciated.
Nick,
Please note that the above procedure will create the column with a double qoutes ("ID","NM") hence the error occurred. Please find the updated code and check if the issue has resolved.
According to oracle=>
Oracle is case sensitive in column and table names. It just converts everything to upper case by default. But if you use names in double quotes, you tell Oracle to create the column in the exact spelling you provided (lower case in the CREATE statement).
Code:
create or replace procedure table_creation(table_name in varchar2,col1 varchar2,col2 varchar2)
is
l_stat varchar2(3000);
v_stat varchar2(1000);
a varchar2(10):='1';
b varchar2(10):='Dummy';
begin
l_stat:='create table '||table_name||' ('||col1||' varchar2(10),'||col2||' varchar2(10))';
execute immediate l_stat;
execute immediate 'insert into '||table_name||'('||col1||','||col2||') values (:x,:y)' using a,b;
end;
/
Note: I have not touched any other logic of the code. Please try and let us know the result.
Only change is
From :
l_stat:='create table '||table_name||' ("'||col1||'" varchar2(10),"'||col2||'" varchar2(10))';
to :
l_stat:='create table '||table_name||' ('||col1||' varchar2(10),'||col2||' varchar2(10))';

Create stored procedure error in oracle

I create a extremely basic stored procedure but oracle toad always shows "Warning: compiled but with compilation errors".
Here is the code:
CREATE OR REPLACE PROCEDURE update_history (
tab1_name IN VARCHAR2
)
AS
BEGIN
END;
Oracle does not handle the empty block; so you can simply add a NULL statement:
CREATE OR REPLACE PROCEDURE update_history (
tab1_name IN VARCHAR2
)
AS
BEGIN
null;
END;

Sending multiple sets of parameters to procedure

I'm using vb.net and oracle db, and currently I have a stored-procedure that is called from my code. Right now it looks similar to this:
CREATE OR REPLACE PROCEDURE MYPROCEDURE(
param1 table.field1%TYPE,
param2 table.field2%TYPE,
param3 table.field3%TYPE,
param4 varchar2,
output OUT number) AS
BEGIN
DO STUFF
END;
I want to ask if it is possible to change this to send multiple sets of parameters at once, so I could use a FOR LOOP inside my procedure to minimize the number of calls. I want to achieve something like this:
CREATE OR REPLACE PROCEDURE MYPROCEDURE(
param myArray
output OUT number) AS
BEGIN
FOR i IN 1..myArray.COUNT LOOP
UPDATE FIELD FROM TABLE WHERE ID = myArray(i).field1;
END LOOP;
END;
Or if there's anything else that would work the same it would be great.
Many thanks.
Yes you can pass a list of objects as parameter in oracle procedure. First you must create the datatype of this list of objects, but you can't do this inside a procedure you have to define it as an oracle object. For example:
CREATE OR REPLACE TYPE TEST."MY_TYPE" AS OBJECT
(PARAM1 VARCHAR (20), PARAM2 NUMBER);
Unfortunately you can define dynamic datatypes inside objects (table.field1%TYPE), but I think you know what datatype this field have.
Second, create a package that have the list of parameter and procedure definition like this:
CREATE OR REPLACE PACKAGE ARRAY_EXAMPLE2
AS
TYPE COL IS TABLE OF MY_TYPE;
PROCEDURE PROCESS_ARRAY (ArrayIn IN COL);
END;
And finally the package implementation
CREATE OR REPLACE PACKAGE BODY ARRAY_EXAMPLE2
AS
PROCEDURE PROCESS_ARRAY (ArrayIn IN COL)
IS
BEGIN
FOR i IN 1 .. ArrayIn.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE ('Hello ' || ArrayIn (i).PARAM1);
END LOOP;
END;
END;
You can try it using this lines of code:
BEGIN
ARRAY_EXAMPLE2.
PROCESS_ARRAY (
array_example2.
COL (MY_TYPE ('Peter', 12),
MY_TYPE ('Jorge', 4),
MY_TYPE ('Bryan', 5)));
END;

trouble defining weakly defined ref cursor

I'm attempting to write a stored proc that takes in a number, n, and returns the first n results for a given query, exclusively locking those n rows. I'm a little new to SQL and I'm having a bit of difficulty matching data types correctly.
My package spec looks like this:
PACKAGE package IS
Type out_result_type is REF CURSOR;
PROCEDURE stored_proc
(in_n IN NUMBER DEFAULT 10,
out_list IN OUT out_result_type);
I then define the cursor in the procedure body, like so:
CURSOR OUT_RESULT_TYPE IS
SELECT a.id
FROM schema.table a
WHERE (some conditions) AND rownum <= in_n;
A bit later on I then try to extract the results of the cursor into the output variable:
OPEN OUT_RESULT_TYPE;
FETCH OUT_RESULT_TYPE INTO out_list; -- error on this line
CLOSE OUT_RESULT_TYPE;
But alas this code doesn't compile; oracle complains that out_list has already been defined with a conflicting data type. Any idea how I can resolve this issue? It's driving me crazy!
Thanks in advance.
CREATE OR REPLACE PACKAGE pkg_test
AS
TYPE tt_cur IS REF CURSOR;
PROCEDURE prc_cur (retval OUT tt_cur);
END;
CREATE OR REPLACE PACKAGE BODY pkg_test
AS
PROCEDURE prc_cur (retval OUT tt_cur)
AS
BEGIN
OPEN retval
FOR
SELECT *
FROM dual;
END;
END;
If you want to lock, use:
CREATE OR REPLACE PACKAGE BODY pkg_test
AS
PROCEDURE prc_cur (retval OUT tt_cur)
AS
BEGIN
OPEN retval
FOR
SELECT a.id
FROM schema.table a
WHERE (some conditions)
AND rownum <= in_n
ORDER BY
column
-- Never forget ORDER BY!
FOR UPDATE;
END;
END;
Two remarks:
A cursor doesn't lock.
You don't have to do Type out_result_type is REF CURSOR;, use default type sys_refcursor. See here: Oracle - How to have an out ref cursor parameter in a stored procedure?
Your out_list must be of wrong type. Consider (script run on 10.2.0.3):
CREATE TABLE t AS SELECT ROWNUM ID FROM all_objects WHERE ROWNUM <= 100;
CREATE OR REPLACE PACKAGE cursor_pck AS
TYPE out_result_type is REF CURSOR;
PROCEDURE stored_proc (p_in IN NUMBER DEFAULT 10,
p_out_list IN OUT out_result_type);
END cursor_pck;
/
If you want to select and lock the rows at the same time you would use the FOR UPDATE clause:
CREATE OR REPLACE PACKAGE BODY cursor_pck AS
PROCEDURE stored_proc (p_in IN NUMBER DEFAULT 10,
p_out_list IN OUT out_result_type) IS
BEGIN
OPEN p_out_list FOR SELECT a.id FROM t a WHERE ROWNUM <= p_in FOR UPDATE;
END stored_proc;
END cursor_pck;
/
With the following setup, you will call the procedure like this:
SQL> SET SERVEROUTPUT ON;
SQL> DECLARE
2 l_cursor cursor_pck.out_result_type;
3 l_id t.id%TYPE;
4 BEGIN
5 cursor_pck.stored_proc(3, l_cursor);
6 LOOP
7 FETCH l_cursor INTO l_id;
8 EXIT WHEN l_cursor%NOTFOUND;
9 dbms_output.put_line(l_id);
10 END LOOP;
11 END;
12 /
1
2
3
PL/SQL procedure successfully completed
This is not going to work the way it's written, because
out_list expects a cursor, not a cursor result.
The name out_result_type is already used for a type, so you can't redefine it to be a cursor in the same scope.
Oracle provides a pre-defined weak reference cursor: sys_refcursor. In usage it would look like:
CREATE OR REPLACE PACKAGE pkg_test
AS
PROCEDURE prc_cur (p_retval OUT sys_refcursor,
p_lookup IN VARCHAR2);
END pkg_test;
CREATE OR REPLACE PACKAGE BODY pkg_test
AS
PROCEDURE prc_cur(p_retval OUT sys_refcursor
p_lookup IN VARCHAR2)
IS
BEGIN
OPEN retval FOR SELECT a.value
FROM tblname a
WHERE a.id <= p_lookup;
END prc_cur;
END pkg_test;
This saves you the trouble of needing to declare a type. The sys_refcursor is a pointer to a result set from an open cursor. If you are familiar with Java, it's the same concept as the java.sql.ResultSet object which provides a way to get at the results of a query.