如何批量删除Oracle表中的大量数据

时间:2014-11-14 12:50:10

标签: sql database oracle oracle10g

我使用的是Oracle 10g,并希望从人员表中删除大约500万条记录(共有1500万条记录),这些记录在订单表中没有任何引用(总共有500万条记录)

由于一次性检测500万条记录会导致撤消日志问题,我决定批量删除100k。我使用以下查询:

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

现在的问题是,这个查询需要花费相同的时间来执行100k记录,因为它需要500万条记录,因为仍然会对两个表进行全表扫描和连接。

是否有一种有效的方法来重写此查询以加快执行速度? 或者用更好的连接条件替换NOT EXISTS子句? 或者使用一些更好的方法将记录限制在100k?

P.S。这只是一次性操作,我不能使用任何DDL操作,但是pl / sql很好

4 个答案:

答案 0 :(得分:4)

根据我的经验,删除大量行的最快方法是:

解决方案1(由Tom Kyte推荐)

`SET TRANSACTION USE ROLLBACK SEGMENT <your_seg>
 DELETE FROM <tab1> WHERE <cond>
 COMMIT`

OR

解决方案2

`create table new_table unrecoverable as select * from old_table where ....;
drop table old_table;
rename new_table to old_table;
create index old_table_idx1 on old_table(c1,c2) unrecoverable parallel 5;
`

我在不同的上下文中使用了第二种解决方案:删除大量行总是最快的。

另一种方法是将数据放入分区中删除然后删除分区(每个分区都有自己的回滚段,可以使用并行性,......)。

答案 1 :(得分:3)

如果希望此查询运行得更快,请添加以下两个索引:

 create index idx_persons_city_pid on persons(city, p_id);
 create index idx_orders_pid on orders(p_id);

答案 2 :(得分:2)

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;
/

答案 3 :(得分:0)

还有一种删除方式:

begin
  dbms_errlog.create_error_log('PERSONS');
end;
/

-- index on foreign key is useful thing in many cases, not only now
create index idx_orders_pid on orders(p_id); 

declare 
  min_id number;
  max_id number;
begin
  select min(p_id), max(p_id)
    into min_id, max_id
    from persons;

  for i in min_id..max_id loop
    delete from persons where p_id between i and i + 100000
    log errors into err$_persons reject limit unlimited;
  end loop;
end;
/

drop table err$_persons;