使用 PL/SQL 删除大量记录
Deleting a large number of records using PL/SQL
我想使用 PL/SQL 删除大量记录。记录由标识上次修改记录的日期字段标识。我不想消耗太多资源,所以我认为我应该限制被删除的记录数,在我看来伪列 ROWNUM 可以达到这个目的。然后我检查受更新影响的行数并重复直到受影响的行数为 0。
我正在寻找有关执行此操作的最佳实践的建议。我也很担心我收到的警告:
"A loop that contains DML statements should be refactored to use BULK COLLECT and FORALL."
但是当我 google 这个话题时,这似乎不适用于我正在尝试做的事情——或者我正在做吗?
欢迎您提出意见和建议。
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;
谢谢
您担心消耗哪些资源?单个 DELETE
语句将是最有效的方法*。假设这是需要定期完成的事情,那么数据库的大小确实应该根据 UNDO
table 空间适当调整,以允许您执行单个 DELETE
。
实际上,退一步说,最有效的方法是用 UPDT_TIMESTMP
对 table 进行分区,然后删除较旧的分区。但是分区是企业版许可证之上的额外成本选项,并且分区 table 可能会对系统产生其他影响。
如果您真的非常需要通过临时提交批量删除行,这似乎是一个非常合理的实现。如果单个 DELETE
语句占用了我夜间处理的很大一部分 window 并且我担心 DELETE
可能会在几个小时后强制回滚和重新开始整个过程。批量删除会比正常删除单个 DELETE
慢,但重新启动会更容易。
使用 BULK COLLECT
和 FORALL
的建议在这种特殊情况下没有意义。它适用于更常见的情况,即某人从一个或多个源 table 中选择数据,在 PL/SQL 中进行一些处理,然后将数据写出到目标 table。通过批量操作而不是通过缓慢的逐行处理来做到这一点会更有效。但是作为单个 INSERT ... SELECT
.
来做会更有效率
我想使用 PL/SQL 删除大量记录。记录由标识上次修改记录的日期字段标识。我不想消耗太多资源,所以我认为我应该限制被删除的记录数,在我看来伪列 ROWNUM 可以达到这个目的。然后我检查受更新影响的行数并重复直到受影响的行数为 0。
我正在寻找有关执行此操作的最佳实践的建议。我也很担心我收到的警告:
"A loop that contains DML statements should be refactored to use BULK COLLECT and FORALL."
但是当我 google 这个话题时,这似乎不适用于我正在尝试做的事情——或者我正在做吗?
欢迎您提出意见和建议。
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;
谢谢
您担心消耗哪些资源?单个 DELETE
语句将是最有效的方法*。假设这是需要定期完成的事情,那么数据库的大小确实应该根据 UNDO
table 空间适当调整,以允许您执行单个 DELETE
。
实际上,退一步说,最有效的方法是用 UPDT_TIMESTMP
对 table 进行分区,然后删除较旧的分区。但是分区是企业版许可证之上的额外成本选项,并且分区 table 可能会对系统产生其他影响。
如果您真的非常需要通过临时提交批量删除行,这似乎是一个非常合理的实现。如果单个 DELETE
语句占用了我夜间处理的很大一部分 window 并且我担心 DELETE
可能会在几个小时后强制回滚和重新开始整个过程。批量删除会比正常删除单个 DELETE
慢,但重新启动会更容易。
使用 BULK COLLECT
和 FORALL
的建议在这种特殊情况下没有意义。它适用于更常见的情况,即某人从一个或多个源 table 中选择数据,在 PL/SQL 中进行一些处理,然后将数据写出到目标 table。通过批量操作而不是通过缓慢的逐行处理来做到这一点会更有效。但是作为单个 INSERT ... SELECT
.