我有一个包含25亿条记录的数据库表。 有1100万的重复。 删除这1100万条记录的最快方法是什么?
答案 0 :(得分:20)
从许多人那里删除一个副本是一件棘手的事情,有了这么多记录,你就会遇到问题。
一种选择是解决问题,并将要保留的记录复制到新表中。您可以使用CREATE TABLE AS SELECT DISTINCT ... NOLOGGING
语法,该语法将复制重复数据删除的记录而不使用事务日志,这要快得多。填充新表后,删除/重命名旧表,并将新表重命名到位。
哦,记得在新表上打一个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
行时生成的UNDO
和2G
信息比删除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)
如果您确定不改变数据的完整性(参照完整性),请禁用约束(索引,其他约束),执行删除,然后启用约束。您必须先尝试一下,看看启用时刷新索引是否比启用它们的删除时间更短。
某些查询优化也可能有所帮助,但我们在不知道更多细节的情况下,我们正在讨论理论。