views:

94

answers:

4

I know that fetching a cursor will give me access to variables like %ROWCOUNT, %ROWTYPE, %FOUND, %NOTFOUND, %ISOPEN

...but I was wondering if there are any other reasons to use

Open - Fetch - Close instructions to loop a cursor

rather than

Loop the cursor with a FOR cycle... (In my opinion this is better becase it is simple)

What do you think?

+4  A: 

The OPEN / FETCH / CLOSE is called explicit cursor syntax; the latter is called implicit cursor syntax.

One key difference you've already noticed is that you can't use %FOUND/%NOTFOUND/etc in implicit cursors... Another thing to be aware of is that implicit cursors are faster than explicit ones--they read ahead (~100 records?) besides not supporting the explicit logic.

Additional info:

OMG Ponies
Pardon me maybe I'm mistaken, but implicit cursors are "select ... into" statements while the explicit ones are the two different syntaxes mentioned in the question: "fetch into..." and "for ... loop". Please correct me if I'm wrong
andr
@andr: No, `SELECT ... INTO` syntax is how Oracle populates variables. But `SELECT ... INTO` is preferable to implicit cursors, if there's the opportunity.
OMG Ponies
SELECT..INTO is for single row selects ONLY. It has nothing to do with explicit/implicit cursors. Explicit cursors are, well, cursors that are explicitly opened, fetched, and closed. Implicit cursors have these actions performed "behind the scenes".
DCookie
@DCookie @OMG Ponies Thank you for clarification, I got a little confused with the terminology.
andr
@DCookie (and OMG) I believe you have this exactly the wrong way round: SELECT...INTO uses an implicit cursor, as do INSERT, UPDATE and DELETE. A FOR loop requires an explicit cursor, but opens and closes it, er, implicitly. It is complicated! See http://download.oracle.com/docs/cd/B10500_01/appdev.920/a96624/06_ora.htm
Tony Andrews
I see the error of my ways, and stand corrected. Interestingly, you can have a FOR with an implicit cursor as well: FOR (SELECT * FROM emp) LOOP uses implicit cursors. Thanks for the correction, Tony.
DCookie
@Tony Andrews: Thanks, appreciate the correction.
OMG Ponies
+2  A: 

I don't know about any crucial differences in this two realizations besides one: for ... loop implicitly closes the cursor after the loop is finished and if open ... fetch ... close syntax you'd rather close the cursor yourself (just a good manner) - thought this is not a necessity: Oracle will close the cursor automatically outbound the visibility scope. Also you can't use %FOUND and %NOTFOUND in for ... loop cursors.

As for me I find the for ... loop realization much easier to read and support.

andr
+10  A: 

From a performance standpoint, the difference is a lot more complicated than the Tim Hall tip that OMG Ponies linked to would imply. I believe that this tip is an introduction to a larger section that has been excerpted for the web-- I expect that Tim went on to make most if not all of these points in the book. Additionally, this entire discussion depends on the Oracle version you're using. I believe this is correct for 10.2, 11.1, and 11.2 but there are definitely differences if you start going back to older releases.

The particular example in the tip, first of all, is rather unrealistic. I've never seen anyone code a single-row fetch using an explicit cursor rather than a SELECT INTO. So the fact that SELECT INTO is more efficient is of very limited practical importance. If we're discussing loops, the performance we're interested in is how expensive it is to fetch many rows. And that's where the complexity starts to come in.

Oracle introduced the ability to do a BULK COLLECT of data from a cursor into a PL/SQL collection in 10.1. This is a much more efficient way to get data from the SQL engine to the PL/SQL collection because it allows you to minimize context shifts by fetching many rows at once. And subsequent operations on those collections are more efficient because your code can stay within the PL/SQL engine.

In order to take maximum advantage of the BULK COLLECT syntax, though, you generally have to use explicit cursors because that way you can populate a PL/SQL collection and then subsequently use the FORALL syntax to write the data back to the database (on the reasonable assumption that if you are fetching a bunch of data in a cursor, there is a strong probability that you are doing some sort of manipulation and saving the manipulated data somewhere). If you use an implicit cursor in a FOR loop, as OMG Ponies correctly points out, Oracle will be doing a BULK COLLECT behind the scenes to make the fetching of the data less expensive. But your code will be doing slower row-by-row inserts and updates because the data is not in a collection. Explicit cursors also offer the opportunity to set the LIMIT explicitly which can improve performance over the default of 100 for an implicit cursor in a FOR loop.

In general, assuming that you're on 10.2 or greater and that your code is fetching data and writing it back to the database,

Fastest

  1. Explicit cursors doing a BULK COLLECT into a local collection (with an appropriate LIMIT) and using FORALL to write back to the database.
  2. Implicit cursors doing a BULK COLLECT for you behind the scenes along with single-row writes back to the datbase.
  3. Explicit cursors that are not doing a BULK COLLECT and not taking advantage of PL/SQL collections.

Slowest

On the other hand, using implicit cursors gets you quite a bit of the benefit of using bulk operations for very little of the upfront cost in refactoring old code or learning the new feature. If most of your PL/SQL development is done by developers whose primary language is something else or who don't necessarily keep up with new language features, FOR loops are going to be easier to understand and maintain than explicit cursor code that used all the new BULK COLLECT functionality. And when Oracle introduces new optimizations in the future, it's far more likely that the implicit cursor code would get the benefit automatically while the explicit code may require some manual rework.

Of course, by the time you're troubleshooting performance to the point where you really care about how much faster different variants of your looping code might be, you're often at the point where you would want to consider moving more logic into pure SQL and ditching the looping code entirely.

Justin Cave
+1, Nice answer. In practice, I have seen the benefits of item 1 over item 2 in your list.
DCookie
+1 though I am surprised that you say "I've never seen anyone code a single-row fetch using an explicit cursor rather than a SELECT INTO". I have seen it a LOT, and it irks me greatly. I personally blame Steve Feuerstein who at one time (he has since retracted) used to write that an explcit cursor SHOULD always be used instead of SELECT..INTO, for performance reasons.
Tony Andrews
+1  A: 

Correct me if I'm wrong but I think both have one nice feature what other one doesn't have.

With for loop you can do like this:

for i in (select * from dual)
  dbms_output.put_line('ffffuuu');
end loop;

And with open .. fetch you can do like this:

declare
  cur sys_refcursor;
  tmp dual.dummy%type;
begin
  open cur for 'select dummy from dual';
  loop
    fetch cur into tmp;
    exit when cur%notfound;
    dbms_output.put_line('ffffuuu');
  end loop;
  close cur;
end;

So with open fetch you can use dynamic cursors but with for loop you can define normal cursor without declaration.

oocce