如何使用分离的事务从Oracle表中删除大量数据?

时间:2015-04-19 21:38:09

标签: sql plsql liquibase

我需要从Oracle表中删除大约5百万条记录。 由于性能(REDO日志),我想删除每笔交易100000条记录,如下所示:

DECLARE
 v_limit PLS_INTEGER :=100000;

CURSOR person_deleted_cur
IS 
 SELECT rowid 
   FROM Persons p
  WHERE City = 'ABC'
   AND NOT EXISTS
               (SELECT O_Id
                  FROM Orders o
                 WHERE p.P_Id = o.P_Id);

TYPE person_deleted_nt IS TABLE OF person_deleted_cur%ROWTYPE
        INDEX BY PLS_INTEGER;
BEGIN
  OPEN person_deleted_cur;
    LOOP
      FETCH person_deleted_cur 
        BULK COLLECT INTO person_deleted_nt LIMIT v_limit;

    FORALL indx IN 1 .. person_deleted_nt.COUNT 
      DELETE FROM Persons WHERE rowid=person_deleted_nt(indx);

    EXIT WHEN person_deleted_cur%NOTFOUND;

   END LOOP;

   CLOSE person_deleted_cur;
  COMMIT;
END;

但Liquibase在一个事务中运行changeSet,如果有任何错误则将其回滚。在Liquibase脚本中明显使用 COMMIT 是一个好习惯吗? 什么应该写得很好?

2 个答案:

答案 0 :(得分:0)

在书中" Oracle专业人士" Tom Kyte写了关于其他交易的更新。关键是:如果您可以使用一个查询更改表,那么这样做。因为一个查询将比不同的事务更快或plsql循环与分区删除。 另一种方法是将CREATE TABLENOLOGGING一起使用,而不是UPDATE/DELETE。这是更改多行的最佳解决方案。

所以用你的查询创建nologging表,而不是删除原始表并重新创建索引,约束等,而不是将temp table重命名为原始表。

答案 1 :(得分:0)

同意@jimmbraddock,但是对于OLTP系统来说影响较小的更简单的解决方案可能是重复运行此查询,直到它不再影响行:

DELETE FROM Persons p
WHERE City = 'ABC'
AND NOT EXISTS
           (SELECT O_Id
              FROM Orders o
             WHERE p.P_Id = o.P_Id)
AND ROWNUM <= 100000;

总资源使用率将高于单个删除,因此如果您的系统可以容纳单个删除仍然会更好,但这将非常强大,并且具有人员索引(city,p_id)和一个订单(p_id)它应该是非常高效的。