Monday, March 28, 2011

PL/SQL Implicit Cursors

Whenever a SQL statement is directly in the execution or exception section of a PL/SQL block, you are working with implicit cursors. These statements include INSERT, UPDATE, DELETE, and SELECT INTO statements. Unlike explicit cursors, implicit cursors do not need to be declared, OPENed, FETCHed, or CLOSEd.
SELECT statements handle the %FOUND and %NOTFOUND attributes differently from explicit cursors. When an implicit SELECT statement does not return any rows, PL/SQL immediately raises the NO_DATA_FOUND exception and control passes to the exception section. When an implicit SELECT returns more than one row, PL/SQL immediately raises the TOO_MANY_ROWS exception and control passes to the exception section.
Implicit cursor attributes are referenced via the SQL cursor. For example:
BEGIN    UPDATE activity SET last_accessed := SYSDATE   
 WHERE UID = user_id;     IF SQL%NOTFOUND THEN      
 INSERT INTO activity_log (uid,last_accessed)       
VALUES (user_id,SYSDATE);    END IF END;
SQL Attributes
Description
%ISOPEN
Always FALSE since the cursor is opened implicitly and closed immediately after the statement is executed.
%FOUND
NULL before the statement.
TRUE if one or more rows were inserted, updated, or deleted or if only one row was selected.
FALSE if no row was selected, updated, inserted, or deleted.
%NOTFOUND
NULL before the statement.
TRUE if no row was selected, updated, inserted, or deleted.
FALSE if one or more rows were inserted, updated, or deleted.
%ROWCOUNT
The number of rows affected by the cursor.
%BULK_ROWCOUNT (Oracle8 i)
A pseudo index-by table containing the numbers of rows affected by the statements executed in bulk bind operations. See the "Bulk Binds (Oracle8 i )" section for more information on %BULK_ROWCOUNT.
Use the RETURNING clause in INSERT, UPDATE, and DELETE statements to obtain data modified by the associated DML statement. This clause allows you to avoid an additional SELECT statement to query the results of the DML statement. For example:
BEGIN    UPDATE activity SET last_accessed := SYSDATE    WHERE UID = user_id 
   RETURNING last_accessed, cost_center     INTO timestamp, chargeback_acct;

1.9.2.1 The SELECT FOR UPDATE clause

By default, the Oracle RDBMS locks rows as they are changed. To lock all rows in a result set, use the FOR UPDATE clause in your SELECT statement when you OPEN the cursor, instead of when you change the data. Using the FOR UPDATE clause does not require you to actually make changes to the data; it only locks the rows when opening the cursor. These locks are released on the next COMMIT or ROLLBACK. As always, these row locks do not affect other SELECT statements unless they, too, are FOR UPDATE. The FOR UPDATE clause is appended to the end of the SELECT statement and has the following syntax:
SELECT ...   FROM ...    FOR UPDATE [OF column_reference] [NOWAIT];
where column_reference is a comma-delimited list of columns that appear in the SELECT clause. The NOWAIT keyword tells the RDBMS to not wait for other blocking locks to be released. The default is to wait forever.
In the following example, only columns from the inventory (pet) table are referenced FOR UPDATE, so no rows in the dog_breeds (dog) table are locked when hounds_in_stock_cur is opened:
DECLARE    CURSOR hounds_in_stock_cur IS       SELECT pet.stock_no, 
pet.breeder, dog.size         FROM dog_breeds dog ,inventory pet        
WHERE dog.breed = pet.breed          AND dog.class = 'HOUND'          
FOR UPDATE OF pet.stock_no, pet.breeder; BEGIN

1.9.2.2 The WHERE CURRENT OF clause

UPDATE and DELETE statements can use a WHERE CURRENT OF clause if they reference a cursor declared FOR UPDATE. This syntax indicates that the UPDATE or DELETE should modify the current row identified by the FOR UPDATE cursor. The syntax is:
[UPDATE | DELETE ] ...    WHERE CURRENT OF cursor_name;
By using WHERE CURRENT OF, you do not have to repeat the WHERE clause in the SELECT statement. For example:
DECLARE    CURSOR wip_cur IS       SELECT acct_no, enter_date FROM wip  
      WHERE enter_date < SYSDATE -7          FOR UPDATE; BEGIN    
FOR wip_rec IN wip_cur    LOOP       INSERT INTO acct_log 
(acct_no, order_date)          VALUES (wip_rec.acct_no, wip_rec.enter_  
           date);       DELETE FROM wip           WHERE CURRENT OF wip_cur; 
   END LOOP; END; 

1.9.3 Cursor Variables

A cursor variable is a data structure that points to a cursor object, which in turn points to the cursor's result set. You can use cursor variables to more easily retrieve rows in a result set from client and server programs. You can also use cursor variables to hide minor variations in queries.
The syntax for a REF_CURSOR type is:
TYPE ref_cursor_name IS REF CURSOR     [RETURN record_type];
If you do not include a RETURN clause, then you are declaring a weak REF CURSOR. Cursor variables declared from weak REF CURSORs can be associated with any query at runtime. A REF CURSOR declaration with a RETURN clause defines a "strong" REF CURSOR. A cursor variable based on a strong REF CURSOR can be associated with queries whose result sets match the number and datatype of the record structure after the RETURN at runtime.
To use cursor variables, you must first create a REF_CURSOR type, then declare a cursor variable based on that type.
The following example shows the use of both weak and strong REF CURSORs:
DECLARE    -- Create a cursor type based on the companies          
table.    TYPE company_curtype IS REF CURSOR        
RETURN companies%ROWTYPE;     -- Create the variable based on the REF CURSOR.
    company_cur company_curtype;     -- And now the weak, general approach. 
   TYPE any_curtype IS REF CURSOR;    generic_curvar any_curtype;
The syntax to OPEN a cursor variable is:
OPEN cursor_name FOR select_statement;
FETCH and CLOSE a cursor variable using the same syntax as for explicit cursors. There are a number of restrictions on cursor variables:
  • Cursor variables cannot be declared in a package since they do not have a persistent state.
  • You cannot use the FOR UPDATE clause with cursor variables.
  • You cannot assign NULLs to a cursor variable nor use comparison operators to test for equality, inequality, or nullity.
  • Neither database columns nor collections can store cursor variables.
  • You cannot use RPCs to pass cursor variables from one server to another.
  • Cursor variables cannot be used with the dynamic SQL built-in package DBMS_SQL.

No comments:

Post a Comment