How to execute an oracle procedure with an out sys_refcursor parameter? - sql

I have a proc in my package body:
create or replace package body MYPACKAGE is
procedure "GetAllRules"(p_rules out sys_refcursor)
is
begin
open p_rules for
select * from my_rules;
end "GetAllRules";
-- etc
And I'm exposing this in my package spec.
How do I execute this procedure in a new SQL Window in PL SQL Developer (or similar)?

You can execute the procedure relatively easily
DECLARE
l_rc sys_refcursor;
BEGIN
mypackage."GetAllRules"( l_rc );
END;
Of course, that simply returns the cursor to the calling application. It doesn't do anything to fetch the data from the cursor, to do something with that data, or to close the cursor. Assuming that your goal is to write some data to dbms_output (which is useful sometimes for prototyping but isn't something that production code should be relying on), you could do something like
DECLARE
l_rc sys_refcursor;
l_rec my_rules%rowtype;
BEGIN
mypackage."GetAllRules"( l_rc );
LOOP
FETCH l_rc INTO l_rec;
EXIT WHEN l_rc%NOTFOUND;
dbms_output.put_line( <<print data from l_rec>> );
END LOOP;
CLOSE l_rc;
END;
If you're really doing something like this with the cursor in PL/SQL, I'd strongly suggest returning a strongly-typed ref cursor rather than a weakly-typed one so that you can declare a record in terms of the cursor's %rowtype rather than forcing the caller to know exactly what type to declare and hoping that the query in the procedure doesn't change. This also requires you to explicitly write code to display the data which gets annoying.
If you're using SQL*Plus (or something that supports some SQL*Plus commands), you can simplify things a bit
VARIABLE rc REFCURSOR;
EXEC mypackage."GetAllRules"( :rc );
PRINT :rc;
As an aside, I'm not a fan of using case-sensitive identifiers. It gets very old to have to surround identifiers like "GetAllRules" with double-quotes every time you want to call it. Unless you have really compelling reasons, I'd suggest using standard case-insensitive identifiers. It's perfectly reasonable to capitalize identifiers reasonably in your code, of course, it just doesn't make a lot of sense to go to the effort of forcing them to be case-sensitive in the data dictionary.

Related

Executing a stored procedure inside another stored procedure using Select Query

I want to call a stored procedure on every row of a table inside my own stored procedure. In SQL Developer workspace I can simple call it like this:
SELECT my_stored_proc(...)
FROM my_table
However in my stored procedure this doesn't seem to execute:
stmt := 'SELECT my_stored_proc(...) FROM my_table';
EXECUTE IMMEDIATE stmt;
This does work:
DECLARE
l_cursor sys_refcursor;
BEGIN
OPEN l_cursor FOR stmt;
LOOP
FETCH l_cursor INTO ...;
EXIT WHEN l_cursor%NOTFOUND;
my_stored_proc(...);
END LOOP;
...
My question is why is this happening.
The behaviour in your second example is explicitly covered in a prominent note in the documentation:
If dynamic_sql_statement is a SELECT statement, and you omit both into_clause and bulk_collect_into_clause, then execute_immediate_statement never executes. For example, this statement never increments the sequence:
EXECUTE IMMEDIATE 'SELECT S.NEXTVAL FROM DUAL'
Which is essentially what you are doing. Because you don't select the result of your procedure (or, in fact your function) into a variable, the query is not executed, so the function is not called.
As you have multiple rows in your table you can't select into a scalar variable, though you could bulk collect into a collection; or use a cursor as you do in your third example. (Although, if the cursor is being opened with the same statement, you seem to be calling the function twice for each row - once when the query is executed, and then again inside the PL/SQL loop. I'm probably reading too much into your contrived example...)
The answer is simple, yet complicated, and many beginner PLSQL developers seem to not be able to get a grasp on the whole concept of cursors...
Your
SELECT my_stored_proc(...)
FROM my_table
in your SQL Developer executes OK because your SQL Developer opens a cursor for your query, fetches the cursor into your data grid, then closes the cursor.
Your
stmt := 'SELECT my_stored_proc(...) FROM my_table';
EXECUTE IMMEDIATE stmt;
inside a stored procedure fails for the same reason - it opens a cursor for the dynamic SQL, yet it does not have any PLSQL variable to fetch the cursor's results into. If you want to select something, you need to
either open a cursor for it, fetch the data into a PLSQL variable in a loop (and do whatever you want to do with the fetched data), then close the cursor,
or to select/bulk select the query results into a PLSQL variable (and then do whatever you want to do with the fetched data).
... which is what you did in your third code snippet.

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.

Call Function within User-Defined Function in SQL

I want to include the following function inside of another user-defined function in Oracle.
DBMS_STATS.GATHER_TABLE_STATS(SCHEMA_IN,TABLE_IN)
SCHEMA_IN and TABLE_IN are arguments to the user-defined function. However, I get the following error.
ORA-14552: cannot perform a DDL, commit or rollback inside a query or DML
How can I resolve this? Below is my SQL script.
CREATE OR REPLACE Function GET_COLUMNS (SCHEMA_IN IN VARCHAR2, NAME_IN IN VARCHAR2)
RETURN VARCHAR2
is
L_TEXT VARCHAR2(32767) := NULL;
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(SCHEMA_IN,NAME_IN);
FOR CUR_REC IN (SELECT COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME = name_in AND NUM_NULLS = 0) LOOP
L_TEXT := L_TEXT || ',' || CUR_REC.COLUMN_NAME;
END LOOP;
return(ltrim(l_text,','));
END;
gather_table_stats is a procedure, not a function. And it a procedure that includes transaction control logic (presumably, a commit at least). You cannot, therefore, call it in a function that is called from SQL. You could call your function from PL/SQL rather than SQL,
DECLARE
l_text varchar2(4000);
BEGIN
l_text := get_columns( <<schema>>, <<table>> );
END;
I would, however, be very, very dubious about the approach you're taking.
First, dbms_stats gathers statistics that are used by the optimizer. Using those statistics in other contexts is generally dangerous. Most dbms_stats calls involve some level of indeterminism-- you're generally gathering data from a sample of rows and extrapolating. That is perfectly appropriate for giving the optimizer information so that it can judge things like roughly how many rows a table scan will return. It may not be appropriate if you're trying to differentiate between a column that is never NULL and one that is very rarely NULL. Some samples may catch a NULL value, others may not. It may seem to work correctly for months or years and then start to fail either consistently or intermittantly.
Second, when you gather fresh statistics, you're potentially forcing Oracle to do hard parses on all the existing SQL statements that reference the table. That can be a major performance hit if you do this in the middle of the day. If you happen to force a query plan to change in a bad way, you'll likely cause the DBA a great deal of grief. If the DBA is gathering statistics in a particular way (locking statistics on some tables, forcing histograms on others, forcing a lack of histograms on others, etc.) to deal with performance issues, it's highly likely that you'll be either working at cross purposes or actively breaking the other.
Third, if a column never has NULL values, it really ought to be marked as NOT NULL. Then you can simply look at the data dictionary to see which columns are nullable and which are not without bothering to gather statistics.
You need to set your function to be an autonomous transaction to execute gather table stats:
CREATE OR REPLACE Function GET_COLUMNS (SCHEMA_IN IN VARCHAR2, NAME_IN IN VARCHAR2)
RETURN VARCHAR2
as
pragma autonomous_transaction;
L_TEXT VARCHAR2(32767) := NULL;
BEGIN

Ad hoc querying Oracle PL/SQL - for SQL Server developer

I used to do Oracle development many many years ago. I have spent most of the past 15 years doing mainly SQL Server, and where I have done Oracle work, been insulated from the workings by Web services etc.
My question seems to have been asked on the web a few times, but it seems difficult somehow to communicate - at least judging by the answers. I very much appreciate that tools and functionality differ, know I have to learn new things, but this is so simple, yet seems so hard to do.
I am looking to do some ad-hoc queries on the database. At the moment we are using SQL Navigator - I am open to using other tools...
In SQL Server Management Studio, if you open a query window, type a bit of SQL that retuns a value or a set, you get a nice display of the rows or values in a results window.
I've gathered that with Oracle PL/SQL things are a bit different, worked out that I need to return a cursor - but how do I get a tool to display the results?
I started simple:
declare
my_id number := 356655;
cursor c1 is select my_id from dual;
begin
open c1;
end;
This runs fine - but how do I see the results? Are there any tools that deal with this as 'nicely' as SSMS? I am used to being able to do a lot of this, including stuff like
(perhaps not exactly the right syntax? but you get the idea...)
declare
my_id number := 356655;
cursor c1 is select name from my_table where id = my_id;
begin
open c1;
And having the results displayed to me as text/grid. Ideally that there is a nice solution. Some spiffy new tool, maybe?
With SQL Developer or SQL*Plus you can use a bind variable declared before the PL/SQL block:
variable rc refcursor;
declare
my_id number := 356655;
begin
open :rc for select my_id from dual;
end;
/
print rc
RC
-------------------------------
356655
You can also use a bind variable within the query, which can be useful:
variable my_id number;
variable rc refcursor;
execute :my_id := 356655;
begin
open :rc for select :my_id from dual;
end;
/
print rc
The variable and print commands are covered in the SQL*Plus documentation, which largely applies to SQL Developer as well - that has its own documentation, including the commands that are carried over from SQL*Plus.
If you have a function that returns a ref cursor then you can call that in a query, as select func(val) from dual, and then the results can go in a grid; or you can call the function (or procedure) with the same :rc bind variable and print it. But I'm not sure either is helpful if you are only doing ad hoc queries.
On the other hand, using a PL/SQL block for an ad hoc query seems a little heavy-handed, even if your queries are complicated. You'd need a good reason to open a cursor for a select statement from within a block, rather than just running the select directly. (Not sure if that's a SQL Server thing or if you actually have a real need to do this!). If you're just running a query inside the block, you don't need the block, even if you want to keep a bind variable for the values you're using in the query:
variable my_id number;
execute :my_id := 356655;
select :my_id from dual;
:MY_ID
----------
356655
I use Oracle SQL Developer.
Anyway, this should work in any oracle sql client:
If you just want to see your results, you can use
dbms_output.put_line('Foo' || somevar || ' bar');
Before this, run
SET SERVEROUTPUT ON
Check the examples at docs.oracle.com
I would suggest using sql developer available free from the oracle website. There is a button which allows you to run sql as a script which will get back what you want. SSMS doesn't work with pl/sql.

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

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??).