Get resultset from oracle stored procedure - oracle

I'm working on converting a stored procedure from SQL server to Oracle.
This stored procedure provides a direct resultset. I mean that if you call the stored procedure in eg Management Studio you directly obtain the resultset.
By converting to Oracle I walk against the problem that I in Oracle will not display the resultset
I searched on the Internet and have seen that the stored procedure should yield a REF CURSOR, but I still walk with the problem to write a little piece of code to obtain the resultset en process that.
Pseudo Code:
Call stored procedure and obtain cursor
Do something with that cursor so that my resultset appears
Someone an idea?

In SQL Plus:
SQL> create procedure myproc (prc out sys_refcursor)
2 is
3 begin
4 open prc for select * from emp;
5 end;
6 /
Procedure created.
SQL> var rc refcursor
SQL> execute myproc(:rc)
PL/SQL procedure successfully completed.
SQL> print rc
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ----------- ---------- ---------- ----------
7839 KING PRESIDENT 17-NOV-1981 4999 10
7698 BLAKE MANAGER 7839 01-MAY-1981 2849 30
7782 CLARKE MANAGER 7839 09-JUN-1981 2449 10
7566 JONES MANAGER 7839 02-APR-1981 2974 20
7788 SCOTT ANALYST 7566 09-DEC-1982 2999 20
7902 FORD ANALYST 7566 03-DEC-1981 2999 20
7369 SMITHY CLERK 7902 17-DEC-1980 9988 11 20
7499 ALLEN SALESMAN 7698 20-FEB-1981 1599 3009 30
7521 WARDS SALESMAN 7698 22-FEB-1981 1249 551 30
7654 MARTIN SALESMAN 7698 28-SEP-1981 1249 1400 30
7844 TURNER SALESMAN 7698 08-SEP-1981 1499 0 30
7876 ADAMS CLERK 7788 12-JAN-1983 1099 20
7900 JAMES CLERK 7698 03-DEC-1981 949 30
7934 MILLER CLERK 7782 23-JAN-1982 1299 10
6668 Umberto CLERK 7566 11-JUN-2009 19999 0 10
9567 ALLBRIGHT ANALYST 7788 02-JUN-2009 76999 24 10

Oracle is not sql server. Try the following in SQL Developer
variable rc refcursor;
exec testproc(:rc2);
print rc2

My solution was to create a pipelined function. The advantages are that the query can be a single line:
select * from table(yourfunction(param1, param2));
You can join your results to other tables or filter or sort them as you please..
the results appear as regular query results so you can easily manipulate them.
To define the function you would need to do something like the following:
-- Declare the record columns
TYPE your_record IS RECORD(
my_col1 VARCHAR2(50),
my_col2 varchar2(4000)
);
TYPE your_results IS TABLE OF your_record;
-- Declare the function
function yourfunction(a_Param1 varchar2, a_Param2 varchar2)
return your_results pipelined is
rt your_results;
begin
-- Your query to load the table type
select s.col1,s.col2
bulk collect into rt
from your_table s
where lower(s.col1) like lower('%'||a_Param1||'%');
-- Stuff the results into the pipeline..
if rt.count > 0 then
for i in rt.FIRST .. rt.LAST loop
pipe row (rt(i));
end loop;
end if;
-- Add more results as you please....
return;
end find;
And as mentioned above, all you would do to view your results is:
select * from table(yourfunction(param1, param2)) t order by t.my_col1;

Hi I know this was asked a while ago but I've just figured this out and it might help someone else. Not sure if this is exactly what you're looking for but this is how I call a stored proc and view the output using SQL Developer.
In SQL Developer when viewing the proc, right click and choose 'Run' or select Ctrl+F11 to bring up the Run PL/SQL window. This creates a template with the input and output params which you need to modify. My proc returns a sys_refcursor. The tricky part for me was declaring a row type that is exactly equivalent to the select stmt / sys_refcursor being returned by the proc:
DECLARE
P_CAE_SEC_ID_N NUMBER;
P_FM_SEC_CODE_C VARCHAR2(200);
P_PAGE_INDEX NUMBER;
P_PAGE_SIZE NUMBER;
v_Return sys_refcursor;
type t_row is record (CAE_SEC_ID NUMBER,FM_SEC_CODE VARCHAR2(7),rownum number, v_total_count number);
v_rec t_row;
BEGIN
P_CAE_SEC_ID_N := NULL;
P_FM_SEC_CODE_C := NULL;
P_PAGE_INDEX := 0;
P_PAGE_SIZE := 25;
CAE_FOF_SECURITY_PKG.GET_LIST_FOF_SECURITY(
P_CAE_SEC_ID_N => P_CAE_SEC_ID_N,
P_FM_SEC_CODE_C => P_FM_SEC_CODE_C,
P_PAGE_INDEX => P_PAGE_INDEX,
P_PAGE_SIZE => P_PAGE_SIZE,
P_FOF_SEC_REFCUR => v_Return
);
-- Modify the code to output the variable
-- DBMS_OUTPUT.PUT_LINE('P_FOF_SEC_REFCUR = ');
loop
fetch v_Return into v_rec;
exit when v_Return%notfound;
DBMS_OUTPUT.PUT_LINE('sec_id = ' || v_rec.CAE_SEC_ID || 'sec code = ' ||v_rec.FM_SEC_CODE);
end loop;
END;

In SQL Plus:
SQL> var r refcursor
SQL> set autoprint on
SQL> exec :r := function_returning_refcursor();
Replace the last line with a call to your procedure / function and the contents of the refcursor will be displayed

FYI as of Oracle 12c, you can do this:
CREATE OR REPLACE PROCEDURE testproc(n number)
AS
cur SYS_REFCURSOR;
BEGIN
OPEN cur FOR SELECT object_id,object_name from all_objects where rownum < n;
DBMS_SQL.RETURN_RESULT(cur);
END;
/
EXEC testproc(3);
OBJECT_ID OBJECT_NAME
---------- ------------
100 ORA$BASE
116 DUAL
This was supposed to get closer to other databases, and ease migrations.
But it's not perfect to me, for instance SQL developer won't display it nicely as a normal SELECT.
I prefer the output of pipeline functions, but they need more boilerplate to code.
more info:
https://oracle-base.com/articles/12c/implicit-statement-results-12cr1

CREATE OR REPLACE PROCEDURE SP_Invoices(p_nameClient IN CHAR)
AS
BEGIN
FOR c_invoice IN
(
SELECT CodeInvoice, NameClient FROM Invoice
WHERE NameClient = p_nameClient
)
LOOP
dbms_output.put_line('Code Invoice: ' || c_invoice.CodeInvoice);
dbms_output.put_line('Name Client : ' || c_invoice.NameClient );
END LOOP;
END;
Executing in SQL Developer:
BEGIN
SP_Invoices('Perico de los palotes');
END;
-- Or:
EXEC SP_Invoices('Perico de los palotes');
Output:
> Code Invoice: 1
> Name Client : Perico de los palotes
> Code Invoice: 2
> Name Client : Perico de los palotes

Related

ORA-00923: FROM keyword not found where expected, parameters in AS clause

SELECT
LMD0011M.CKEY1 AS CDNAME
,LMD0011M.CKEY1 || '\:' || LMD0011M.CDTA1 AS NMNAME
FROM
LMD0011M LMD0011M
The above query works fine but when I change the column names to parameters... I get an "ORA-00923: FROM keyword not found where expected" error message.
SELECT
LMD0011M.CKEY1 AS :CDNAME
,LMD0011M.CKEY1 || '\:' || LMD0011M.CDTA1 AS :NMNAME
FROM
LMD0011M LMD0011M
:CDNAME = CDNAME
:NMNAME = NMNAME
Even if I run the query in Oracle SQL Developer, I'm getting the error. I'm using Java Hibernate. How to solve this problem? Help pls
It would probably help if you described why you want to dynamically change column aliases. Personally, I don't see any valid cause to do that, but hey - if you do, no objections.
From my point of view, it is PL/SQL you need and dynamic SQL. Pass desired aliases to the procedure and return refcursor which reflects what you've just passed. Here's an example:
SQL> create or replace procedure p_test
2 (alias_1 in varchar2, alias_2 in varchar2, par_rc out sys_refcursor)
3 is
4 l_str varchar2(1000);
5 begin
6 l_str := 'select ename as "' || dbms_assert.simple_sql_name(alias_1) ||'"'||
7 ', job as "' || dbms_assert.simple_sql_name(alias_2) ||'"'||
8 ' from emp where deptno = 20';
9 open par_rc for l_str;
10 end;
11 /
Testing:
SQL> var rc refcursor
SQL> exec p_test('liTTle', 'f00t', :rc);
PL/SQL procedure successfully completed.
SQL> print :rc
liTTle f00t
---------- ---------
SMITH CLERK
JONES MANAGER
SCOTT ANALYST
ADAMS CLERK
FORD ANALYST
SQL>
SQL> exec p_test('brian', 'antiquena', :rc);
PL/SQL procedure successfully completed.
SQL> print :rc
BRIAN ANTIQUENA
---------- ---------
SMITH CLERK
JONES MANAGER
SCOTT ANALYST
ADAMS CLERK
FORD ANALYST
SQL>

Oracle: wrong number or types of arguments in call

create procedure employee_get
(
c_Emp out SYS_REFCURSOR
)
is
begin
open c_Emp for
select a.last_name, b.department_id, b.department_name from employee a inner join department b on a.department_id = b.department_id
order by a.last_name asc ;
end employee_get;
-- run
var c_out refcursor;
exec employee_get(:c_out);
print :c_out;
Everything was fine until I made the Run command. I'm new member, please help me. Thanks and respect!
You need following code to execute the procedure:
create procedure employee_get
(
c_Emp out SYS_REFCURSOR
)
is
begin
open c_Emp for
select a.last_name, b.department_id, b.department_name from employee a inner join department b on a.department_id = b.department_id
order by a.last_name asc ;
end employee_get;
/
-- EXECUTE THE PROCEDURE
DECLARE
C_OUT SYS_REFCURSOR;
BEGIN
-- run
employee_get(C_OUT);
dbms_sql.return_result(C_OUT); --UPDATED
END;
/
Cheers!!
The procedure which returns refcursor:
SQL> create or replace procedure employee_get (c_emp out sys_refcursor) is
2 begin
3 open c_emp for select empno, ename from emp;
4 end;
5 /
Procedure created.
Execution:
SQL> declare
2 c_out sys_refcursor;
3 begin
4 employee_get(c_out);
5 dmbs_sql.return_result(c_out);
6 end;
7 /
dmbs_sql.return_result(c_out);
*
ERROR at line 5:
ORA-06550: line 5, column 3:
PLS-00201: identifier 'DMBS_SQL.RETURN_RESULT' must be declared
ORA-06550: line 5, column 3:
PL/SQL: Statement ignored
See with your DBA whether you can get execute privileges on DBMS_SQL package.
If not:
SQL> var c_out refcursor;
SQL> exec employee_get(:c_out);
PL/SQL procedure successfully completed.
SQL> print :c_out
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.
SQL>

Is it possible to pass a column as a parameter to a ODER BY clause?

I want to use a function in Oracle. But I need to ORDER BY accordingly with what the user pass as parameter.
Example:
FUNCTION foo_function(p_date IN DATE, p_column_number IN NUMBER)
RETURN foo_bar
IS
BEGIN
SELECT * FROM bar WHERE date = p_date ORDER BY p_column_number
<...other code...>
END;
This block does not work.
Is it possible to do something like this to pass the column as parameter to the ORDER BY clause?
UPDATE ----------
#XING shows a very good way to solve the problem without DECODE like the answers of other questions.
But the problem is that I'm missing something.
Now I'm getting this error: "inconsistent datatypes: expected %s got %s"
-- Creating my object with 2 columns.
CREATE OR REPLACE TYPE MY_OBJECT AS OBJECT(IDOP NUMBER, EMISSION_DATE DATE);
-- Creating the table of MY_OBJECT type.
CREATE OR REPLACE TYPE TB_OBJECT AS TABLE OF MY_OBJECT;
-- Creating my function
CREATE OR REPLACE FUNCTION GET_OBJECT(
P_INITIAL_DATE IN DATE,
P_FINAL_DATE IN DATE,
P_COLUMN_NUMBER IN NUMBER)
RETURN TB_OBJECT
IS
V_TB TB_OBJECT;
V_SQL VARCHAR2(1000);
BEGIN
V_SQL :=
'SELECT IDOP, EMISSION_DATE FROM OP WHERE EMISSION_DATE BETWEEN :p_initial_date AND :p_final_date ORDER BY :p_column_number';
EXECUTE IMMEDIATE V_SQL
BULK COLLECT INTO V_TB
USING P_COLUMN_NUMBER;
RETURN (V_TB);
END;
-- Calling the function
SELECT * FROM TABLE (GET_OBJECT(TO_DATE('01/06/2017','dd/MM/yyyy'), TO_DATE('09/01/2018', 'dd/MM/yyyy'), 1));
UPDATE (2) ---------------------
The problem in my code is that I forget to cast the select to my object.
This way the code works.
V_SQL :=
'SELECT MY_OBJECT(IDOP, EMISSION_DATE) FROM OP WHERE EMISSION_DATE BETWEEN :p_initial_date AND :p_final_date ORDER BY :p_column_number';
Thank you #XING
Here in the below example i show you how to do that in a function by only ordering only 1 column. You can modify the function as per your need;
--Created a type of number to return the ordered result. You can create an object with the column same as used in your select statement.
CREATE OR REPLACE TYPE VAR_RET AS TABLE OF NUMBER;
/
--Function
CREATE OR REPLACE FUNCTION FOO_FUNCTION (
P_COLUMN_NUMBER IN NUMBER)
RETURN VAR_RET
IS
V_RES VAR_RET;
v_sql VARCHAR2(1000);
BEGIN
V_SQL :=
'SELECT EMPNO FROM EMP ORDER BY :p_column_number'; --<-- Create Object with all the columns you are selecting here and then a type of your object to hold result
EXECUTE IMMEDIATE V_SQL
BULK COLLECT INTO V_RES
USING P_COLUMN_NUMBER;
RETURN (V_RES);
END;
--Result:
SQL> select * from table( FOO_FUNCTION (1));
COLUMN_VALUE
------------
7369
7499
7521
7566
7654
7698
7782
7788
7839
7844
7876
EDIT: WITH 2 COLUMNS Ordering
CREATE OR REPLACE TYPE MY_TAB AS OBJECT
(
V_EMPNM VARCHAR2(100),
V_EMPNO NUMBER
);
CREATE OR REPLACE TYPE VAR_RET AS TABLE OF MY_TAB;
/
CREATE OR REPLACE FUNCTION FOO_FUNCTION (
P_COLUMN_NUMBER IN NUMBER)
RETURN VAR_RET
IS
V_RES VAR_RET;
v_sql VARCHAR2(1000);
BEGIN
V_SQL := --You need to cast your select statement as per Object
'SELECT MY_TAB(ENAME,EMPNO) FROM EMP ORDER BY :p_column_number'; --<-- Create Object with all the columns you are selecting here and then a type of your object to hold result
EXECUTE IMMEDIATE V_SQL
BULK COLLECT INTO V_RES
USING P_COLUMN_NUMBER;
RETURN (V_RES);
END;
Execution:
1) Way 1
DECLARE
VAR VAR_RET := VAR_RET ();
BEGIN
VAR := FOO_FUNCTION (1);
FOR I IN 1 .. VAR.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE (VAR (I).V_EMPNM ||' ' ||VAR (I).V_EMPNO);
END LOOP;
END;
SQL> /
SMITH 7369
ALLEN 7499
WARD 7521
JONES 7566
MARTIN 7654
BLAKE 7698
CLARK 7782
SCOTT 7788
KING 7839
TURNER 7844
ADAMS 7876
JAMES 7900
FORD 7902
MILLER 7934
PL/SQL procedure successfully completed.
2) WAY 2
select * from table( FOO_FUNCTION (1));
V_EMPNM V_EMPNO
----------------------------------------------------------------------------------------------------
SMITH 7369
ALLEN 7499
WARD 7521
JONES 7566
MARTIN 7654
BLAKE 7698
CLARK 7782
SCOTT 7788
KING 7839
TURNER 7844
ADAMS 7876
V_EMPNM V_EMPNO
----------------------------------------------------------------------------------------------------
JAMES 7900
FORD 7902
MILLER 7934
14 rows selected.

Oracle PL/SQL collect values from a loop into a cursor

I have a PL/SQL TABLE TYPE result set that contains document ids.
I can loop over the result set without a problem, but the issue is that I have to return a sys_refcursor from the function, but I am unable to collect the values from the loop into the cursor.
TYPE table_typ IS TABLE OF DOCUMENT_QUEUE.ENV_ID%TYPE INDEX BY PLS_INTEGER;
FUNCTION GET_DOCS()
RETURN SYS_REFCURSOR
IS
LS_CUR SYS_REFCURSOR;
LR_UPDATED_ROWS table_typ;
BEGIN
UPDATE DOCUMENT_QUEUE DQ
...
RETURNING DQ.ENV_ID BULK COLLECT INTO LR_UPDATED_ROWS;
-- Need to collect all of the following rows into the cursor
FOR indx IN NVL (LR_UPDATED_ROWS.FIRST, 0) .. NVL (LR_UPDATED_ROWS.LAST, -1)
LOOP
SELECT * FROM DOCUMENT_QUEUE DQ WHERE DQ.ENV_ID = LR_UPDATED_ROWS(indx);
END LOOP;
RETURN LS_CUR;
END GET_DOCS;
All help and hints are welcome.
FOR indx IN NVL (LR_UPDATED_ROWS.FIRST, 0) .. NVL (LR_UPDATED_ROWS.LAST, -1)
LOOP
SELECT * FROM DOCUMENT_QUEUE DQ WHERE DQ.ENV_ID = LR_UPDATED_ROWS(indx);
END LOOP;
RETURN LS_CUR;
You do not need the cursor FOR LOOP. You could use OPEN CURSOR FOR statement and return a SYS_REFCURSOR.
For example,
OPEN LS_CUR FOR SELECT * FROM DOCUMENT_QUEUE DQ
WHERE DQ.ENV_ID IN (SELECT * FROM TABLE(LR_UPDATED_ROWS));
RETURN LS_CUR;
or,
OPEN LS_CUR FOR SELECT * FROM DOCUMENT_QUEUE DQ
WHERE DQ.ENV_ID MEMBER OF LR_UPDATED_ROWS;
RETURN LS_CUR;
However, in order to do that, you must CREATE the type at SQL level not at PL/SQL level. Else, you would receive PLS-00642: local collection types not allowed in SQL statements.
A small demo:
Create the type at SQL level:
SQL> CREATE OR REPLACE TYPE table_typ AS TABLE OF NUMBER
2 /
Type created.
Let's get the output in SQL*Plus using a refcursor:
Using MEMBER OF syntax:
SQL> variable r refcursor
SQL> DECLARE
2 l_typ table_typ;
3 TYPE numbers IS TABLE OF NUMBER;
4 n numbers;
5 BEGIN
6 SELECT empno BULK COLLECT INTO l_typ FROM emp;
7 OPEN :r FOR SELECT empno,
8 ename FROM emp WHERE empno member OF l_typ;
9 END;
10 /
PL/SQL procedure successfully completed.
SQL> print r
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.
Using TABLE function:
SQL> variable r refcursor
SQL> DECLARE
2 l_typ table_typ;
3 TYPE numbers IS TABLE OF NUMBER;
4 n numbers;
5 BEGIN
6 SELECT empno BULK COLLECT INTO l_typ FROM emp;
7 OPEN :r FOR SELECT empno,
8 ename FROM emp WHERE empno IN (SELECT * from TABLE(l_typ));
9 END;
10 /
PL/SQL procedure successfully completed.
SQL> print r
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.
For the mentioned requirement i have mentioned below a snippet which will help to fetch all the rows into ref cursor for every rowid. Let me know if this helps.
CREATE OR REPLACE TYPE table_typ
IS
TABLE OF DOCUMENT_QUEUE.ENV_ID%TYPE INDEX BY PLS_INTEGER;
CREATE OR REPLACE FUNCTION GET_DOCS
RETURN SYS_REFCURSOR
IS
LS_CUR SYS_REFCURSOR;
LR_UPDATED_ROWS table_typ;
lv_rows_lst VARCHAR2(32676);
BEGIN
SELECT <COL1> BULK COLLECT INTO LR_UPDATED_ROWS FROM <TABLE_NAME>;
FOR I IN LR_UPDATED_ROWS.FIRST..LR_UPDATED_ROWS.LAST
LOOP
lv_rows_lst:=lv_rows_lst||','||LR_UPDATED_ROWS(I);
END LOOP;
lv_rows_lst:=SUBSTR(lv_rows_lst,2,LENGTH(lv_rows_lst));
OPEN LS_CUR FOR 'SELECT * FROM DOCUMENT_QUEUE DQ WHERE DQ.ENV_ID IN ('||lv_rows_lst||')';
RETURN LS_CUR;
END GET_DOCS;

PLSQL select query in BEGIN block

CREATE OR REPLACE PROCEDURE validate_date
AS
strABC DATE;
strDummy VARCHAR2(20);
CURSOR C_Example IS SELECT * from dummytable;
BEGIN
FOR R_Example IN C_RetNxWeek LOOP
strABC := R_RetNxWeek.something;
strDummy := R_RetNxWeek.something;
END LOOP;
IF (something < something) THEN
ELSE
strDummy:= SELECT * FROM sometable WHERE something = something; <-----alternative to do this?
END IF;
END validate_date;
I have a very basic store procedure template like above, i have a cursor that will select some record from a table, in the IF ELSE statement in BEGIN block, i wish to do a checking on a data by selecting a table, since i cannot put cursor in BEGIN block, how can i do that?
The select statement result cannot be stored as you stated,
it should be done via
It should use INTO Clause please refer https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/returninginto_clause.htm
No need of an explicit cursor, use a CURSOR FOR LOOP. I don't know your requirement, since I think it could be done using plain SQL. However, if you really want to use PL/SQL, you can make use of BULK COLLECT and FORALL.
And regarding the SELECT, you need to use INTO clause in PL/SQL.
SQL> DECLARE
2 eno NUMBER;
3 STR_DUMMY VARCHAR2(20);
4 BEGIN
5 FOR i IN
6 (SELECT * FROM emp
7 )
8 LOOP
9 eno := i.empno;
10 dbms_output.put_line(eno);
11 END LOOP;
12 SELECT ENAME INTO STR_DUMMY FROM EMP WHERE EMPNO = 7788;
13 dbms_output.put_line(STR_DUMMY);
14 END;
15 /
7369
7499
7521
7566
7654
7698
7782
7788
7839
7844
7876
7900
7902
7934
SCOTT
PL/SQL procedure successfully completed.
SQL>

Resources