Difference between "IN" and "IN OUT" CURSOR parameter in Oracle - sql

From Oracle:
"When you declare a cursor variable as the formal parameter of a subprogram that fetches from the cursor variable, you must specify the IN or IN OUT mode. If the subprogram also opens the cursor variable, you must specify the IN OUT mode."
But, I can code that (only OUT parameter):
create or replace procedure mycur_out(mc OUT mycurpkg.mytypecur) as
begin
open mc for select * from mytable;
end mycur_out;
and works equal to (IN OUT parameter)
create or replace procedure mycur_inout(mc IN OUT mycurpkg.mytypecur)
as
begin
open mc for select * from table10;
end mycur_inout;
Also, It's work fine with dynamic cursor too:
create or replace procedure mycur_out_ref(mc out mycurpkg.mytyperefcur)
as
begin
open mc for 'select * from table10';
end mycur_out_ref;
I've tested the 3 cases directly from oracle and from VB6 with ADO, and no problems.
So, in that cases, is there any difference between IN using just "OUT" and "IN OUT" cursors parameters?
UPDATE
The reason I'm asking:
We read data using routines similar
to the examples (just open the
cursors). The cursor parameters
always are "IN OUT" (Don't ask me
why, I'm trying to figure out)
The routines are invoked with ADO/VB6
Now, we are trying to use some of the routines from JDBC, but the
adapter apparently just accepts OUT
parameters in this cases.
Finally, the main reason, I want to change the cursor parameters on DB
routines to only OUT, but first I
want to know the collaterals effects
of that change.
Thanks!

In the text you quote from the manual, note that it is specifically talking about "a subprogram that fetches from the cursor variable". None of your examples do this, so the quote is not relevant to them.
However, it nonetheless appears that there's nothing wrong with using OUT only in such a situation, if the subprogram both opens and fetches from the cursor variable:
SQL> variable c refcursor
SQL> set serveroutput on
SQL> create or replace procedure no_good (c OUT sys_refcursor)
2 as
3 my_dummy dual.dummy%type;
4 begin
5 open c for select dummy from dual union all select dummy from dual;
6 fetch c into my_dummy;
7 dbms_output.put_line( my_dummy );
8 end;
9 /
Procedure created.
SQL> exec no_good( :c )
X
PL/SQL procedure successfully completed.
SQL> print c
D
-
X
I think the the text is actually trying to make two points that are somewhat independent of each other. Firstly, if you want to pass any already-opened cursor variable into a subprogram, which will fetch from it, the parameter must be declare IN or IN OUT. Secondly, if you want to pass a cursor variable into a subprogram, which will then open it, the parameter must be declared OUT or IN OUT. This is true regardless of whether you actually care about passing the value of the cursor variable back to the caller:
SQL> create or replace procedure no_good (c IN sys_refcursor)
2 as
3 my_dummy dual.dummy%type;
4 begin
5 open c for select dummy from dual;
6 fetch c into my_dummy;
7 dbms_output.put_line( my_dummy );
8 close c;
9 end;
10 /
Warning: Procedure created with compilation errors.
SQL> show error
Errors for PROCEDURE NO_GOOD:
LINE/COL ERROR
-------- -----------------------------------------------------------------
5/6 PL/SQL: SQL Statement ignored
5/11 PLS-00361: IN cursor 'C' cannot be OPEN'ed
This error can be fixed by changing the parameter mode, but actually it would seem to make more sense to simply make the cursor variable a local variable rather than a parameter.

If I understand the question right, the difference is that with the IN OUT version you can pass in a cursor from outside the procedure, and then change that variable (similar to the difference between OUT and IN OUT for a simple numeric variable).
The OUT parameter cursor starts out as a NULL value / closed cursor.
The IN OUT parameter version starts with whatever state is passed in from outside.
You may want to retry your procedure calls repeatedly passing in the same cursor variable - the OUT version should replace the existing value, the IN OUT version should give an exception on the second time round that you are trying to open an open cursor.
Another thing the IN OUT approach allows, that the OUT approach does not, is to take action based on the passed in cursor, and change the returned cursor.
PROCEDURE lp_test2 (mc IN OUT mycurpkg.mytypecur)
IS
lr table10%ROWTYPE;
BEGIN
IF mc%ISOPEN THEN
FETCH mc INTO lr;
IF mc%NOTFOUND THEN
CLOSE mc;
/* Switch cursor to alternative table */
open mc for select * from schema2.table10;
END IF;
END IF;
END lp_test2;
I am just struggling to think of a real situation where you might want to (take in a cursor variable, cast it back into a SQL statement, append some extra dynamic SQL, and return the whole lot back as the same cursor??).

Related

Run an oracle SQL script twice with different parameters

I have an SQL statement in Oracle SQL developer that has some variables:
DEFINE custom_date = "'22-JUL-2016'" --run1
DEFINE custom_date = "'25-JUL-2016'" --run2
SELECT * FROM TABLE WHERE date=&custom_date
The real query is much more complicated and has many more variables and new tables are created from the results of the query. How can I create a script so that the query is executed twice, the first time with the custom date set as in the first line and the second time as in the second line.
In Oracle, the &variable is a "substitution variable" and is not part of SQL; it is part of the SQL*Plus scripting language (understood by SQL Developer, Toad etc.)
The better option, and what you are asking about, is BIND VARIABLES. The notation is :variable (with a colon : instead of &), for example :custom_date.
The difference is that a substitution variable is replaced by its value in the front-end application (SQL Developer in your case) before the query is ever sent to the Oracle engine proper. A bind variable is substituted at runtime. This has several benefits; discussing them is outside the scope of your question.
When you execute a query with bind variables in SQL Developer, the program will open a window where you enter the desired values for the bind variables. You will have to experiment with that a little bit till you can make it work (for example I never remember if a date must be entered with the single quotes or without). Good luck!
Define is used in TRANSACT SQL. To do this Oracle way, You can create anonymus PL/SQL block, similar to this:
DECLARE
p_param1 DATE;
p_param2 NUMBER;
CURSOR c_cur1(cp_param1 DATE,cp_param2 NUMBER)
IS
SELECT * FROM table WHERE date = cp_param1
;
BEGIN
-- Execute it first time
p_param1 := TO_DATE('2016-09-01','YYYY-MM-DD');
FOR r IN c_cur1(p_param1)
LOOP
NULL;
END LOOP;
-- Execute it second time
p_param1 := TO_DATE('2015-10-11','YYYY-MM-DD');
FOR r IN c_cur1(p_param1)
LOOP
NULL;
END LOOP;
END;
And in it, You create cursor with parameters and execute it twice with different parameter.
I do not know why You want to execute this query twice, so the script abowe does nothing with results, but it certainly should execute Your query twice, with different params.

Is it possible to open cursor which is in package in another procedure?

Just a thought,Is it possible to open cursor which is in package in another procrdure?
example : pack_name has a procedure myprocedure. The cursor opened in this procedure can be opened in another procedure ?
i.e can OPEN LV_TEST_CUR FOR LV_QUERY; be written in another procedure ?
create or replace package pack_name
is
create or replace
PROCEDURE myprocedure
AS
LV_TEST_CUR SYS_REFCURSOR;
LV_QUERY VARCHAR2(200);
LV_DATE DATE;
BEGIN
LV_QUERY:='select sysdate as mydate from dual';
END myprocedure;
end pack_name;
Why would you want to? It doesn't really make sense to define the query used for a ref cursor in one package's procedure and then to open it in a completely different package's procedure.
Sure it's possible (although your attempt won't work) - you'd just be passing around the string containing the select statement you want to use to open your ref cursor - but it's not the best design. For a start, you're now stuck using dynamic sql, when you might otherwise have been able to open the ref cursor with static sql (dynamic sql is only checked at runtime, not at compile time, so you won't know about any syntax errors until you try to open the cursor).
In general, I would stick to opening the ref cursor in the same place as it was defined.

db2 stored procedure returning ref cursor

I am new to db2 stored procedures and have a question about creating a procedure that returns a reference cursor (to be read by a java program).
I am using db2 control center to try and compile the procedure in...
CREATE OR REPLACE PROCEDURE EPOS.REP_MAN_DAILY_TRAN_COUNTS()
DYNAMIC RESULT SETS 1
READS SQL DATA
LANGUAGE SQL
BEGIN
DECLARE c CURSOR WITH RETURN TO CALLER FOR
select 'x' from sysibm.sysdummy1
union
select 'p' from sysibm.sysdummy1;
OPEN c;
END
I get an error saying ...
OPEN c
DB21028E The cursor "C" has not been declared.
But I have declared C... so I am a bit confused. Does this procedure look right? Maybe i can't compile using db2 control center?
thanks
Probably you did not specify the character separator. If you are using the db2 clp, make sure the procedure finishes by a # and then call it like this for a file called procedure.sql
db2 -td# -f procedure.sql

Executing a Stored Procedure in Oracle

I have a stored procedure, on Toad for Oracle I am calling the procedure
using
SELECT FROM PKGName.ProcedureName(1,'10/10/2010','10/23/2010',7,7)
FROM DUAL
I have 3 output parameter on this procedure as well I am getting an
ORA-00904: PKGName.ProcedureName : Invalid Identifier
Do have to mention the output parameter on the procedure call as well? If yes how can I use it?
You cannot use a procedure in a SELECT statement. Functions yes (with appropriate return types), procedures no. Items in a SELECT list must be expressions, which must resolve to a value. A procedure does not meet this criteria.
And yes, you do need to mention the output variables in your parameter list. The procedure is going to set those parameters to some values, there needs to be a output parameter specified for each to receive them. #schurik shows you how it is usually done in PL/SQL. #Datajam is close to how you'd do it in SQL*Plus, but leaves out the output parameters:
SQL> var num_var number
SQL> var txt_var varchar2(15)
SQL> var txt_var2 varchar2(20)
SQL> exec PKGName.ProcedureName(1,'10/10/2010','10/23/2010',7,7, :num_var, :txt_var, :txt_var2);
PL/SQL procedure successfully completed
num_var
---------------
42
txt_var
-----------------
some text
txt_var2
-------------------
some other text
SQL>
declare
-- declare variables to keep output values
output_par_1 varchar2(100);
output_par_2 number(10);
...
begin
PKGName.ProcedureName(1,'10/10/2010','10/23/2010',output_par_1,output_par_2);
-- display output values
dbms_output.put_line('output_par_1: ' || output_par_1);
dbms_output.put_line('output_par_2: ' || output_par_2);
end;
/
If you want to be able to call procedures from select, wrap it with a function or table function. See here for more details: http://technology.amis.nl/blog/1017/calling-stored-procedures-using-plain-sql-for-when-sql-is-allowed-but-calls-to-stored-procedures-are-not (heck the title is almost an article hehehe).
Yes, you must provide all arguments. Declare a variable of the appropriate type and pass it as an output argument.
You shouldn't really call a procedure using a SELECT statement (and even if you did, the call would be before the FROM part).
Instead, use a SQL*Plus prompt (I think Toad has a built-in SQL*Plus interface):
exec PKGName.ProcedureName(1,'10/10/2010','10/23/2010',7,7);
The code in the question is syntactically wrong, it should be
SELECT PKGName.ProcedureName(1,'10/10/2010','10/23/2010',7,7) FROM DUAL
However, this would only work for functions. But as it's obviously working in Toad I assume the poster actually did have a function. I also made the assumption that using SQL was a prerequisite.
If the point of the question was how to make use of multiple output parameters - try creating a user defined type. Maybe the question should then be renamed to "Calling a procedure with output parameters from SQL in Oracle".
Otherwise simple wrapper function without output parameters would do the job.

How do I return all values from a stored procedure?

Forgive my naivety, but I am new to using Delphi with databases (which may seem odd to some).
I have setup a connection to my database (MSSQL) using a TADOConnection. I am using TADOStoredProc to access my stored procedure.
My stored procedure returns 2 columns, a column full of server names, and a 2nd column full of users on the server. It typically returns about 70 records...not a lot of data.
How do I enumerate this stored procedure programmatically? I am able to drop a DBGrid on my form and attach it to a TDataSource (which is then attached to my ADOStoredProc) and I can verify that the data is correctly being retrieved.
Ideally, I'd like to enumerate the returned data and move it into a TStringList.
Currently, I am using the following code to enumerate the ADOStoredProc, but it only returns '#RETURN_VALUE':
ADOStoredProc1.Open;
ADOStoredProc1.ExecProc;
ADOStoredProc1.Parameters.Refresh;
for i := 0 to AdoStoredProc1.Parameters.Count - 1 do
begin
Memo1.Lines.Add(AdoStoredProc1.Parameters.Items[i].Name);
Memo1.Lines.Add(AdoStoredProc1.Parameters.Items[i].Value);
end;
Call Open to get a dataset returned
StoredProc.Open;
while not StoredProc.EOF do
begin
Memo1.Lines.Add(StoredProc.FieldByName('xyz').Value);
StoredProc.Next;
end;
Use Open to get the records from the StoredProc
Use either design-time Fields, ad-hoc Fields grabbed with FieldByName before the loop or Fields[nn] to get the values.
procedure GetADOResults(AStoredProc: TADOStoredProc; AStrings: TStrings);
var
fldServer, fldUser: TField;
begin
AStoredProc.Open;
fldServer := AStoredProc.FieldByName('ServerName');
fldUser := AStoredProc.FieldByName('User');
while not AStoredProc.EOF do
begin
AStrings.Add(Format('Server: %s - / User: %s',[fldServer.AsString, fldUser.AsString]));
// or with FFields and Index (asumming ServerName is the 1st and User the 2nd) and no local vars
AStrings.Add(Format('Server: %s - / User: %s',[AStoredProc.Fields[0].AsString, AStoredProc.Fields[1].AsString]));
AStoredProc.Next;
end;
end;
//use like
GetADOResults(ADOStoredProc1, Memo1.Lines);
Note: Fields[nn] allows to write less code but beware if the StoredProc changes the order of the returned columns.
If your stored procedure returns a result set (rows of data), don't use ExecProc. It's designed to execute procedures with no result set. Use Open or Active instead, and then you can process them just as you are using Parameters:
ADOStoredProc.Open;
for i := 0 to ADOStoredProc1.Parameters.Count - 1 do
begin
Memo1.Lines.Add(ADOStoredProc1.Parameters.Items[i].Name);
Memo1.Lines.Add(ADOStoredProc1.Parameters.Items[i].Value);
end;
BTW, calling Open and then ExecProc causes problems; Open returns a result set, ExecProc then clears it because you're running the procedure a second time with no result set expected. I also don't think you need the Parameters.Refresh, but I'm not 100% sure of that.
Take a look at this (just Googled it):
[http://www.scip.be/index.php?Page=ArticlesDelphi12&Lang=EN#Procedure][1]
Basically, a SQL Server stored procedure always returns one return value, but it can also create a result set, which you need to process like the data set returned from a regular select statement.