Skip to main content

PL/SQL Brain Teaser: how many ways can you define a cursor in PL/SQL?

According to the PL/SQL User Guide,
cursor is a pointer to a private SQL area that stores information about processing a specific SELECT or DML statement.
So how many ways can you define a cursor in PL/SQL?

Let's include in this ways in which you can get Oracle to define a cursor for you, as well.

Please submit a comment with JUST ONE example of a cursor definition. You can submit multiple ideas, but with multiple comments. 

If you include several in a single comment, I will take only the first one that has not already been suggested

Comments

  1. You can define a dynamic cursor a.k.a REF CURSOR

    ReplyDelete
    Replies
    1. Forgot an example :)

      DECLARE
      TYPE t_ref_cur IS REF CURSOR;
      cur_ref t_ref_cur;
      v_sql_stmt VARCHAR2(4000 CHAR);
      BEGIN
      v_sql_stmt := 'SELECT COL1,COL2 FROM TABLE1 WHERE 1 = 1 AND COL1 = :1';

      <>
      OPEN cur_ref FOR v_sql_stmt USING 'blabla';
      LOOP
      FETCH ... BULK COLLECT ...
      EXIT WHEN ...

      DO SOME STUFF
      END LOOP x;
      CLOSE cur_ref;
      END;

      Delete
  2. an implicit cursor in a select statement:
    select dummy from dual

    ReplyDelete
  3. an implicit cursor for an update statement:
    update emp set sal = sal * 1.1

    ReplyDelete
  4. an explicit cursor for a cursor for loop
    declare
    cursor c is
    select empno from emp;
    begin
    for r in c loop
    -- do exciting stuff
    null;
    end loop;
    end;

    ReplyDelete
  5. an implicit cursor for a cursor for loop
    begin
    for r in (select empno from emp) loop
    -- do exciting stuff
    null;
    end loop;
    end;

    ReplyDelete
  6. an explicit cursor for a while loop
    declare
    cursor c is
    select * from emp;
    r c%rowtype;
    begin
    open c;
    fetch c into r;
    while c%found loop
    -- do exciting stuff
    fetch c into r;
    end loop;
    close c;
    end;

    ReplyDelete
  7. explicit cursor:

    declare
    cur_x is
    select ...;

    v_x cur_x%rowtype;
    begin
    open cur_x;
    loop
    fetch
    --do some stuff
    end loop;
    close_cur_x;
    end;

    ReplyDelete
  8. Dynamic cursor definition?

    EXECUTE IMMEDIATE 'SELECT ' || p_col || ' FROM ' || p_table INTO v_variable;

    ReplyDelete
  9. cursor expression:

    SELECT dname
    ,CURSOR ( SELECT ename
    FROM emp e
    WHERE (e.deptno = d.deptno)
    ORDER BY ename)
    FROM dept d
    ORDER BY dname;

    ReplyDelete
  10. Cursor as part of pipelined function? Not sure whether it counts as Christian Chelemen also described something similar.

    select * from table(pip_fnc(cursor(select * from table)));

    ReplyDelete
  11. I went through all comments here and on LinkedIn. Here's the summary:

    Cursor variable (REF CURSOR)
    Implicit cursor: SELECT
    Implicit cursor: UPDATE, DELETE, INSERT
    Explicit cursor in block (DECLARE CURSOR my_cur IS SELECT ...)
    Cursor FOR loop (FOR rec IN (SELECT …))
    Dynamic SQL cursor (EXECUTE IMMEDIATE)
    CURSOR Expression
    FORALL statement (FORALL [UPDATE | DELETE | INSERT ]
    Dynamic SQL cursor (DBMS_SQL.OPEN_CURSOR)

    But there is still at least one way to define a cursor in PL/SQL code that is not shown above. Can anyone figure it out?

    ReplyDelete
  12. I don't see any cursor passing arguments for variables...

    ReplyDelete
  13. I think the CURSOR expression should be excluded from the list. As it really is part of the SQL language. In plsql you can pass this into a variable or parameter of cause. But then this is a cursor variable (sysrefcursor or strong ref cursor).

    ReplyDelete
  14. Implicit result sets were not mentioned yet it seems. However it also does not really create a new cursor. Instead it allows to return a cursor to the caller, e.g. sql*plus.

    Here is an example

    create or replace procedure testReturnResult(cnt in binary_integer)
    authid definer
    is
    myCur sys_refcursor;
    begin
    open myCur for select level num# from dual connect by level <= cnt;
    DBMS_SQL.RETURN_RESULT (myCur);
    end testReturnResult;
    /

    SQL> execute testReturnResult(17);

    PL/SQL-Prozedur erfolgreich abgeschlossen.

    ResultSet #1

    NUM#
    ----------
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    NUM#
    ----------
    12
    13
    14
    15
    16
    17

    17 Zeilen ausgewählt.

    SQL>

    ReplyDelete
  15. @sven, you are right, CURSOR is SQL operator, thanks for clarifying.

    The only other thing I was looking for was defining a cursor in a package specification - it's an explicit cursor, but has a different form from the sort you declare in a block, because you can "hide" the SELECT in the package body:

    CREATE OR REPLACE PACKAGE my_pkg AUTHID DEFINER
    AS
    CURSOR emps_cur RETURN employees%ROWTYPE;
    END;
    /

    CREATE OR REPLACE PACKAGE BODY my_pkg
    AS
    CURSOR emps_cur RETURN employees%ROWTYPE
    IS
    SELECT *
    FROM employees;
    END;
    /

    ReplyDelete

Post a Comment

Popular posts from this blog

Why DBMS_OUTPUT.PUT_LINE should not be in your application code

A database developer recently came across my  Bulletproof PL/SQL  presentation, which includes this slide. That first item in the list caught his attention: Never put calls to DBMS_OUTPUT.PUT_LINE in your application code. So he sent me an email asking why I would say that. Well, I suppose that is the problem with publishing slide decks. All the explanatory verbiage is missing. I suppose maybe I should do a video. :-) But in the meantime, allow me to explain. First, what does DBMS_OUTPUT.PUT_LINE do? It writes text out to a buffer, and when your current PL/SQL block terminates, the buffer is displayed on your screen. [Note: there can be more to it than that. For example, you could in your own code call DBMS_OUTPUT.GET_LINE(S) to get the contents of the buffer and do something with it, but I will keep things simple right now.] Second, if I am telling you not to use this built-in, how could text from your program be displayed on your screen? Not without a lot o...

How to Pick the Limit for BULK COLLECT

This question rolled into my In Box today: In the case of using the LIMIT clause of BULK COLLECT, how do we decide what value to use for the limit? First I give the quick answer, then I provide support for that answer Quick Answer Start with 100. That's the default (and only) setting for cursor FOR loop optimizations. It offers a sweet spot of improved performance over row-by-row and not-too-much PGA memory consumption. Test to see if that's fast enough (likely will be for many cases). If not, try higher values until you reach the performance level you need - and you are not consuming too much PGA memory.  Don't hard-code the limit value: make it a parameter to your subprogram or a constant in a package specification. Don't put anything in the collection you don't need. [from Giulio Dottorini] Remember: each session that runs this code will use that amount of memory. Background When you use BULK COLLECT, you retrieve more than row with each fetch, ...

PL/SQL 101: Three ways to get error message/stack in PL/SQL

The PL/SQL Challenge quiz for 10 September - 16 September 2016 explored the different ways you can obtain the error message / stack in PL/SQL. Note: an error stack is a sequence of multiple error messages that can occur when an exception is propagated and re-raised through several layers of nested blocks. The three ways are: SQLERRM - The original, traditional and (oddly enough) not currently recommended function to get the current error message. Not recommended because the next two options avoid a problem which you are unlikely  to run into: the error stack will be truncated at 512 bytes, and you might lose some error information. DBMS_UTILITY.FORMAT_ERROR_STACK - Returns the error message / stack, and will not truncate your string like SQLERRM will. UTL_CALL_STACK API - Added in Oracle Database 12c, the UTL_CALL_STACK package offers a comprehensive API into the execution call stack, the error stack and the error backtrace.  Note: check out this LiveSQL script if...