EXPLAIN EXTENDED

How to create fast database queries

Oracle: returning deleted records

Comments enabled. I *really* need your comment

From Stack Overflow:

I need to return a rowset from an Oracle procedure, and then delete them in that same procedure.

Is there a neat way of doing this without temp tables?

Something like an in-memory cursor maybe?

Oracle has a nice feature of returning cursor variables from a stored procedure or an anonymous block.

As any cursor, the returned cursor is a handle to a stable rowset.

This means that it's not affected by any changes to database that were made after the cursor was open.

We can exploit this feature to achieve the required behavior.

Let's create a sample table:

CREATE TABLE t_deleter (id INT NOT NULL PRIMARY KEY, value VARCHAR2(50))
/
INSERT
INTO    t_deleter (id, value)
SELECT  level, 'Value ' || level
FROM    dual
CONNECT BY
level <= 10000
/
COMMIT
/

Now, we need to create a procedure that opens a cursor which selects some values and then deletes these values.

I'll make it an anonymous block for the sake of simplicity.

SELECT  COUNT(*)
FROM    t_deleter
/
VAR cur REFCURSOR
BEGIN
        OPEN    :cur FOR
        SELECT  *
        FROM    t_deleter
        WHERE   id <= 10;
        DELETE
        FROM    t_deleter
        WHERE   id <= 10;
END;
/
COMMIT
/
SELECT  COUNT(*)
FROM    t_deleter
/
PRINT cur

The script above, being run in SQL*Plus, will return the following:

  COUNT(*)
----------
     10000

PL/SQL procedure successfully completed.

Commit complete.

  COUNT(*)
----------
      9990


        ID VALUE
---------- --------------------------------------------------
         1 Value 1
         2 Value 2
         3 Value 3
         4 Value 4
         5 Value 5
         6 Value 6
         7 Value 7
         8 Value 8
         9 Value 9
        10 Value 10

10 rows selected.

What happened here?

First, we selected the number of values from t_deleter to make sure everything's in place:

SELECT  COUNT(*)
FROM    t_deleter
/
  COUNT(*)
----------
     10000

Second, we declared a REFCURSOR variable, and then ran the anonymous block than selected first ten records into this REFCURSOR variable.

Then we deleted these records and committed the transaction:

VAR cur REFCURSOR
BEGIN
OPEN    :cur FOR
SELECT  *
FROM    t_deleter
WHERE   id <= 10;
        DELETE
        FROM    t_deleter
        WHERE   id <= 10;
END;
/
COMMIT
/
PL/SQL procedure successfully completed.

Commit complete.

The transaction is committed. The deleted records don't exists anywhere but in the cursor.

Now, we make sure that the records are deleted:

SELECT  COUNT(*)
FROM    t_deleter
/
  COUNT(*)
----------
      9990

, and, finally, select the dead records from the cursor:

PRINT cur
        ID VALUE
---------- --------------------------------------------------
         1 Value 1
         2 Value 2
         3 Value 3
         4 Value 4
         5 Value 5
         6 Value 6
         7 Value 7
         8 Value 8
         9 Value 9
        10 Value 10

10 rows selected.

Here they are, the dead records.

Note that after the cursor is open and fetched, the records are dead forever, they can only be brought back by using Log Mining or restoring from the backup. The cursor cannot be reopen.

Written by Quassnoi

May 13th, 2009 at 11:00 pm

Posted in Oracle

Leave a Reply