如何删除更快?

时间:2009-07-17 12:56:50

标签: database performance oracle plsql

我有一个包含25亿条记录的数据库表。 有1100万的重复。 删除这1100万条记录的最快方法是什么?

5 个答案:

答案 0 :(得分:20)

从许多人那里删除一个副本是一件棘手的事情,有了这么多记录,你就会遇到问题。

一种选择是解决问题,并将要保留的记录复制到新表中。您可以使用CREATE TABLE AS SELECT DISTINCT ... NOLOGGING语法,该语法将复制重复数据删除的记录而不使用事务日志,这要快得多。填充新表后,删除/重命名旧表,并将新表重命名到位。

请参阅http://www.databasejournal.com/features/oracle/article.php/3631361/Managing-Tables-Logging-versus-Nologging.htm

哦,记得在新表上打一个UNIQUE索引,这样就不会再发生了。

故事的寓意是...... 从不使用DELETE来删除大量记录,它的速度非常慢,因为它必须将所有已删除的记录存储在重做日志中。复制和切换,或TRUNCATE。

答案 1 :(得分:3)

DELETE
FROM    mytable
WHERE   rowid IN
        (
        SELECT  rowid
        FROM    (
                SELECT  rowid, ROW_NUMBER() OVER (ORDER BY dupfield) rn
                FROM    mytable r
                )
        WHERE   rn > 1
        )

或者甚至是这样:

DELETE
FROM    mytable mo
WHERE   EXISTS
        (
        SELECT  NULL
        FROM    mytable mi
        WHERE   mi.dup_field = mo.dup_field
                AND mi.rowid <> mo.rowid
        )

这两个查询都会使用非常高效的HASH SEMI JOIN,如果dup_field上没有索引,后者会更快。

您可能想要复制行,但请注意,复制REDO行时生成的UNDO2G信息比删除11M时要多得多。< / p>

答案 2 :(得分:2)

是否删除现有行或创建适当的新表并删除旧表更快取决于很多因素。 1100万行很多,但它只占表中总行数的0.5%。很有可能重新创建&amp; drop可能比删除慢得多,具体取决于源表上存在多少索引,以及数据页上存在需要删除的行的位置。

然后是源表是否存在的问题。如果有插入物和正在进行清理时正在进行的更新,副本&amp;如果没有相当数量的额外代码可以在事后同步表格,那么drop就无法运行。

最后,为什么这项操作必须“快速”?是因为系统在进程发生时需要脱机吗?您可以编写一个在系统处于活动状态时删除欺骗的过程,但在消耗撤消方面不会影响系统的其余部分。我们过去通过首先编写一个查询来收集第二个表中要删除的行的主键来解决这个问题,如下所示:

  INSERT
    INTO RowsToDeleteTable
  SELECT PKColumn
    FROM SourceTable
   WHERE <conditions used to find rows to remove>

CREATE UNIQUE INDEX PK_RowsToDelete ON RowsToDeleteTable (PKColumn);

然后我们有一个PL / SQL块,它可以循环遍历游标中的行,如下所示:

BEGIN
  FOR theRow IN (SELECT PKColumn FROM RowsToDeleteTable ORDER BY 1) LOOP
    <delete source table for theRow.PKColumn)
    <optionally wait a bit>
    commit;
  END LOOP;
END;

或做这样的事情:

BEGIN
  FOR theRow IN (SELECT MIN(PKColumn) FROM RowsToDeleteTable ) LOOP
    <delete source table for theRow.PKColumn)
    <optionally wait a bit>
    DELETE RowsToDeleteTable
     WHERE PKColumn = theRow.PKColumn;
    commit;
  END LOOP;
END;

循环和“SELECT MAX”显然效率较低,但它的优点是允许您跟踪删除操作的进度。我们在循环中放了一些等待代码,以便我们控制收割操作的剧烈程度。

RowsToDeleteTable的初始创建非常快,您可以根据需要允许进程占用。在这种情况下,删除区域中留下的“漏洞”不会太糟糕,因为您删除了总数据的这么小的一小部分。

答案 3 :(得分:1)

首先在定义和包含重复值的一列或多列上添加索引

然后,假设该表有一个主键(PK),

  Delete Table T Where PK <> 
        (Select Min(PK) From Table
         Where ColA = T.ColA
           ...  for each column in set defined above
           And ColB = T.ColB)

注意:也可以使用Max(PK),你所做的就是识别一条不从每组重复项中删除的记录

编辑:为了消除对事务日志和UNDO分区的广泛使用,您可以将dupes的值存储在临时表中,然后在单个事务中删除每对的欺骗...

假设只有一列(称为ColA,一个数字)定义了欺骗......

   Create Table Dupes (ColA Number)
   Insert Dupes(ColA)
   Select Distinct ColA
   From Table
   Group By ColA
   Having Count(*) > 1

   recordExists Number := 0 ;
   ColAValue Number;
   Select Case When Exists (Select Count(*) From Dupes)
   Then 1 Else 0 End Into recordExists From Dual;


   While recordExists = 1 
      Loop 
         Select (Select Max(ColA) From Dupes) 
         Into ColAValue From Dual;
         Begin Transaction
            Delete Table T
            Where ColA = ColAValue
               And pk <> (Select Min(Pk) From Table 
                          Where ColA = ColAValue);
            Delete Dupes Where ColA = ColAValue;
         Commit Transaction;
         Select Case When Exists (Select Count(*) From Dupes)
         Then 1 Else 0 End Into recordExists From Dual;
      End Loop;

未经测试,因此语法可能需要按摩...

答案 4 :(得分:0)

如果您确定不改变数据的完整性(参照完整性),请禁用约束(索引,其他约束),执行删除,然后启用约束。您必须先尝试一下,看看启用时刷新索引是否比启用它们的删除时间更短。

某些查询优化也可能有所帮助,但我们在不知道更多细节的情况下,我们正在讨论理论。