retrieve out parameter from stored procedure? - oracle

I have created one stored procedure in oracle:
PROCEDURE string_opp(input_string IN varchar2,output_string OUT varchar2)
Now the problem is how to execute this stored procedure and retrieve the output parameter.i've followed in sql developer:
SET SERVEROUTPUT ON
DECLARE
outputString VARCHAR;
BEGIN
EXEC string_opp('input String',:outputString);
END;
When i tried this i didn't get anything, could someone help me?

Just a couple of issues:
SET SERVEROUTPUT ON
DECLARE
outputString VARCHAR(20);
BEGIN
string_opp('input String', outputString);
dbms_output.put_line(outputString);
END;
You can use as the same variable:
SET SERVEROUTPUT ON
DECLARE
outputString VARCHAR(20);
BEGIN
outputString := 'input String';
string_opp(outputString);
dbms_output.put_line(outputString);
END;
Just define your procedure parameter as IN OUT in place of just OUT.
Check this resource:
http://psoug.org/snippet/FUNCTIONS-IN-OUT-parameter_873.htm

Let say:
If you have Store procedure with output parameter:
Create procedure test(name out varchar2(50))
as
begin
name:='testing output parameter';
-- dbms_output.put_line('Output parameter value ' || name );
end;
Now execute the above procedure :
declare
l_name varchar2(50);
begin
test(l_name);
dbms_output.put_line( 'name = ' || l_ename );
end;

Related

Simple Oracle stored procedure

I have this SQL query:
select title
from DEPARTMENT;
I tried to write a stored procedure:
create PROCEDURE select_some
(whats VARCHAR2 ,c_select_some OUT SYS_REFCURSOR)
AS
BEGIN
OPEN c_select_some FOR
SELECT whats
FROM department;
END select_some;
/
But where I execute it with "title" parameter I got 8 rows with 'title' instead actual content. What's wrong?
Execution:
var whats varchar2(20)
variable whats = 'Title'
variable mycursor refcursor;
exec select_some (:whats, mycursor);
For this, you need to use dynamic SQL.
Something like this
create or replace procedure select_from_department(
col_name in varchar2,
c_res out sys_refcursor
)
is
l_sql varchar2(300);
begin
l_sql := 'select ' || dbms_assert.simple_sql_name(col_name) || ' from departments';
open c_res for l_sql ;
end;
DEMO

Using collection inside dynamic sql

I am trying to pass dbms_sql.number_table from one procedure to another and then using it inside a dynamic plsql block. But the below code throws error as:
Error(6,17): PLS-00306: wrong number or types of arguments in call to '||'
create or replace
procedure proc1( v_in_table_name varchar2,
v_in_column_name varchar2,
v_in dbms_sql.number_table)
as
plsql_block varchar2(4000);
begin
plsql_block:='declare
begin
FORALL INDX IN 1 ..'||v_in.count||' SAVE EXCEPTIONS
UPDATE '||v_in_table_name||'
Set '||v_in_column_name||'=123 WHERE col2='||v_in||'(INDX)||;
end';
execute immediate plsql_block;
end proc1;
You should make two changes:
First(and its case of error) its when you concatinate string with collection.
If you want to send collection into pl/sql block you shoud use the param.
And second you should add ; in the end of dynamic pl\sql:
create or replace
procedure proc1(v_in dbms_sql.number_table)
as
plsql_block varchar2(4000);
begin
plsql_block:='declare
l_collect dbms_sql.number_table := :number_table ;
begin
FORALL INDX IN 1 ..'||v_in.count||' SAVE EXCEPTIONS
UPDATE table_name
Set col1=123 WHERE col2=l_collect(INDX);
end;';
execute immediate plsql_block using v_in;
end proc1;
But after all changes I would like to ask: Are you realy need to use dynamic pl\sql?
There is no need for using a dynamic block. Also i dont think it can work as well. You can use this way;
create or replace
procedure proc1(v_in dbms_sql.number_table)
as
plsql_block varchar2(4000);
l_collect dbms_sql.number_table;
begin
l_collect := v_in;
FORALL INDX IN 1 ..l_collect.count SAVE EXCEPTIONS
UPDATE table_name
Set col1=123
WHERE col2=l_collect(INDX);
commit;
end proc1;
Execution:
SQL> DECLARE
var DBMS_SQL.number_table;
BEGIN
var (1) := 1;
var (2) := 2;
var (3) := 3;
proc1 (var);
END;
/
PL/SQL procedure successfully completed.
EDIT: As per your edit the code becomes like:
create or replace procedure proc1 (v_in_table_name varchar2,
v_in_column_name varchar2,
v_in dbms_sql.number_table)
as
plsql_block varchar2 (4000);
begin
plsql_block := q'[ FORALL INDX IN 1 ..v_in.count SAVE EXCEPTIONS
UPDATE :table_name
Set :col1=123
WHERE col2=v_in(INDX)]';
execute immediate plsql_block using v_in_table_name, v_in_column_name;
commit;
end proc1;

how do I write a stored procedure where an input parameter gives back a result set? [duplicate]

I have created one stored procedure in oracle:
PROCEDURE string_opp(input_string IN varchar2,output_string OUT varchar2)
Now the problem is how to execute this stored procedure and retrieve the output parameter.i've followed in sql developer:
SET SERVEROUTPUT ON
DECLARE
outputString VARCHAR;
BEGIN
EXEC string_opp('input String',:outputString);
END;
When i tried this i didn't get anything, could someone help me?
Just a couple of issues:
SET SERVEROUTPUT ON
DECLARE
outputString VARCHAR(20);
BEGIN
string_opp('input String', outputString);
dbms_output.put_line(outputString);
END;
You can use as the same variable:
SET SERVEROUTPUT ON
DECLARE
outputString VARCHAR(20);
BEGIN
outputString := 'input String';
string_opp(outputString);
dbms_output.put_line(outputString);
END;
Just define your procedure parameter as IN OUT in place of just OUT.
Check this resource:
http://psoug.org/snippet/FUNCTIONS-IN-OUT-parameter_873.htm
Let say:
If you have Store procedure with output parameter:
Create procedure test(name out varchar2(50))
as
begin
name:='testing output parameter';
-- dbms_output.put_line('Output parameter value ' || name );
end;
Now execute the above procedure :
declare
l_name varchar2(50);
begin
test(l_name);
dbms_output.put_line( 'name = ' || l_ename );
end;

PL/SQL Before-After-Pattern

I would like to use a before-after-pattern approach with PL/SQL (PSEUDO CODE):
Pattern method:
procedure doIt(DO_SOMETHING)
is
l_cnt pls_integer := 1;
begin
loop
begin
DO_SOMETHING;
exit;
exception
when exception changed then
if l_cnt = 2 then
--raise exception...
else
l_cnt := l_cnt + 1;
end if;
end;
end loop;
end;
And execute it like this:
begin
doIt(execute immediate sql_statement using in or out);
end;
As you can see, I would like to use different dynamic sql statements (execute immediate with one or more in and out variables) but always the same before-after-pattern approach.
Have someone an idea how I can solve this problem?
This solution is not so nice, but it works. Perhaps you can start from it and make something better:
I used 2 parameters in the doIt function-
1) the command to execute immediate
2) the arguments as an anydata type
In the execute immediate command I've put all the logic from translating the anydata to some type which I created to wrap the IN OUT parameters.
here is the code:
a type like this should be created for every different command:
create or replace type some_type as object(a number, b number);
/
this is the procedure :
create or replace procedure doIt(aa in varchar2, param IN OUT anydata) is
begin
execute immediate aa using in out param;
end doIt;
/
and this is how I call it (in this example I just selected count(*) from dual into some OUT param):
declare
i number;
prm some_type;
ad anydata;
a number;
b number;
begin
prm := new some_type(a,b);
ad := anydata.convertobject(prm);
doIt('declare prmAd anydata := :0; prm1 some_type; x number; begin x := prmAd.getobject(prm1); select count(*) into prm1.a from dual; :0 := anydata.convertobject(prm1); end;', ad);
i := ad.GetObject(prm);
dbms_output.put_line(prm.a);
end;
basically you can add to the doIt procedure what ever you want, and run with it any command.
I guess you can make things nices- move some of the execute immediate string to the doIt procedure, maybe declare the type better and so on.
I can't find much information on Before/After pattern but looking at what your trying to achieve it could look something like this:
create or replace
procedure doIt(stmt in varchar2, param in varchar2)
is
l_cnt pls_integer := 1;
begin
loop
begin
execute immediate stmt using param;
exit;
exception
when others then
if l_cnt = 2 then
raise;
else
l_cnt := l_cnt + 1;
end if;
end;
end loop;
end;
/
-- run it
exec doIt('insert into my_table (col1) values ( :val1 )', 'richard' );
The problem here is that if you want to pass two parameters then you will have to override 'doIt' eg:
procedure doIt(stmt in varchar2, param1 in varchar2, param2 in varchar2)
...
using param1, param2
Also the other caveat is passing different data types into param. Again you could override the procedure with the correct data type - be warned this could get messy with multiple data types or number of arguments.

PL/SQL print out ref cursor returned by a stored procedure

How can I fetch from a ref cursor that is returned from a stored procedure (OUT variable) and print the resulting rows to STDOUT in SQL*PLUS?
ORACLE stored procedure:
PROCEDURE GetGrantListByPI(p_firstname IN VARCHAR2, p_lastname IN VARCHAR2,
p_orderby IN VARCHAR2, p_cursor OUT grantcur);
PL/SQL:
SET SERVEROUTPUT ON;
DECLARE
TYPE r_cursor IS REF CURSOR;
refCursor r_cursor;
CURSOR grantCursor IS
SELECT last_name, first_name
FROM ten_year_pis
WHERE year_added = 2010;
last_name VARCHAR2(100);
first_name VARCHAR2(100);
BEGIN
OPEN grantCursor;
FETCH grantCursor INTO last_name, first_name;
WHILE grantCursor%FOUND LOOP
PMAWEB_PKG.GetGrantListByPI(last_name, first_name, 'last_name', refCursor);
--HOW DO I LOOP THROUGH THE RETURNED REF CURSOR (refCursor)
--AND PRINT THE RESULTING ROWS TO STDOUT?
FETCH grantCursor into last_name, first_name;
END LOOP;
CLOSE grantCursor;
END;
/
Note: This code is untested
Define a record for your refCursor return type, call it rec. For example:
TYPE MyRec IS RECORD (col1 VARCHAR2(10), col2 VARCHAR2(20), ...); --define the record
rec MyRec; -- instantiate the record
Once you have the refcursor returned from your procedure, you can add the following code where your comments are now:
LOOP
FETCH refCursor INTO rec;
EXIT WHEN refCursor%NOTFOUND;
dbms_output.put_line(rec.col1||','||rec.col2||','||...);
END LOOP;
You can use a bind variable at the SQLPlus level to do this. Of course you have little control over the formatting of the output.
VAR x REFCURSOR;
EXEC GetGrantListByPI(args, :x);
PRINT x;
If you want to print all the columns in your select clause you can go with the autoprint command.
CREATE OR REPLACE PROCEDURE sps_detail_dtest(v_refcur OUT sys_refcursor)
AS
BEGIN
OPEN v_refcur FOR 'select * from dummy_table';
END;
SET autoprint on;
--calling the procedure
VAR vcur refcursor;
DECLARE
BEGIN
sps_detail_dtest(vrefcur=>:vcur);
END;
Hope this gives you an alternate solution
More easier option is to use DBMS_SQL.return_result();
Lets say your package / procedure / cursor spec is as below.
create or replace PACKAGE my_package IS
TYPE my_ref_cursor_type IS REF CURSOR;
PROCEDURE my_procedure (
p_in_param1 IN VARCHAR2,
p_in_param2 IN VARCHAR2,
p_in_param3 IN VARCHAR2,
p_my_ref_cursor OUT my_ref_cursor_type,
p_err_code OUT NUMBER,
p_err_msg OUT VARCHAR2
);
END my_package;
Try this to invoke the procedure from your sql developer WORKSHEET
SET SERVEROUTPUT ON;
DECLARE
P_MY_REF_CURSOR my_schema.my_package.my_ref_cursor_type;
P_ERR_CODE NUMBER;
P_ERR_MSG VARCHAR2(200);
BEGIN
my_package.my_procedure(
'VALUE1',
'VALUE2',
'VALUE3',
P_MY_REF_CURSOR => P_MY_REF_CURSOR,
P_ERR_CODE => P_ERR_CODE,
P_ERR_MSG => P_ERR_MSG
);
DBMS_OUTPUT.PUT_LINE(P_ERR_MSG);
DBMS_OUTPUT.PUT_LINE(P_ERR_CODE);
DBMS_SQL.return_result(P_MY_REF_CURSOR);
END;
Hope this helps !
There are many ways for displaying the sys_refcursor result set and one of them that is so easy is using SQL Developer to fetch sys_refcursor and print output which are:
Create a test function to print its result
Execute the function
View the output
Verify the result set

Resources