Reputation: 79
I want to delete a large number of records using PL/SQL. The records are identified by a DATE field that identifies the last time the record was modified. I don't want to consume too many resources, so I thought that I should limit the number of records that were being deleted and it seemed to me that the pseudo column ROWNUM could server that purpose. I then check the number of rows effected by the update and repeat until the number of rows effected is 0.
I'm looking for a recommendation for the best practice in doing this. I am also concerned about the warning that I am getting:
"A loop that contains DML statements should be refactored to use BULK COLLECT and FORALL."
But when I google the topic, it seems like this doesn't apply to what I am trying to do-or does it?
Your comments and recommendations are welcomed.
CREATE OR REPLACE PACKAGE BODY MY_PURGE
AS
PROCEDURE PURGE_MY_TABLE (v_Cut_Off_Date IN DATE,
C_MAX_DELETE IN NUMBER,
DEL_COUNT OUT NUMBER)
IS
v_RECORDS_DELETED NUMBER := 0;
V_DONE BOOLEAN := FALSE;
BEGIN
DEL_COUNT := 0;
WHILE NOT V_DONE
LOOP
DELETE FROM MYTABLE
WHERE UPDT_TIMESTMP < v_Cut_Off_Date
AND ROWNUM <= C_MAX_DELETE;
v_RECORDS_DELETED := SQL%ROWCOUNT;
DEL_COUNT := DEL_COUNT + v_RECORDS_DELETED;
IF (v_RECORDS_DELETED = 0)
THEN
V_DONE := TRUE;
END IF;
COMMIT;
END LOOP;
END;
Thanks
Upvotes: 2
Views: 2858
Reputation: 231661
What resources are you concerned about consuming? A single DELETE
statement is going to be the most efficient approach*. Assuming this is something that needs to be done regularly, the database should really be sized appropriately in terms of UNDO
tablespace to allow you to do a single DELETE
.
Actually, taking a step back, the most efficient approach would be to partition the table by the UPDT_TIMESTMP
and drop the older partition(s). But partitioning is an extra cost option on top of your enterprise edition license and partitioning the table may have other impacts on the system.
If you really, really need to delete rows in batches with interim commits, this appears to be a pretty reasonable implementation. I would really only consider this if the single DELETE
statement took a substantial fraction of my nightly processing window and I was concerned that the DELETE
might fail after a couple hours forcing a rollback and a restart of the entire process. Deleting in batches would be slower than doing the single DELETE
normally but it would be easier to restart.
The recommendation to use BULK COLLECT
and FORALL
doesn't make sense in this particular case. It would apply to the more common case where someone is selecting data from one or more source tables, doing some processing in PL/SQL, and then writing the data out to a destination table. It would be more efficient to do that via bulk operations rather than via slow row-by-row processing. But it would be even more efficient to do it as a single INSERT ... SELECT
.
Upvotes: 3