我有一个包含25亿条记录的数据库表。
其中有1100万条重复的记录。
删除这1100万条记录最快的方法是什么?
我有一个包含25亿条记录的数据库表。
其中有1100万条重复的记录。
删除这1100万条记录最快的方法是什么?
删除多个重复记录中的一个是一项棘手的任务,如果有很多记录,你就会遇到问题。
其中一种选择是反其道而行之,将要保留的记录复制到一个新表中。您可以使用CREATE TABLE AS SELECT DISTINCT ... NOLOGGING
语法,它将复制您的去重记录而不使用事务日志,这样速度更快。一旦新表填充完毕,删除/重命名旧表,并将新表重命名为原来的表名。
哦,还要记得在新表上打上唯一索引,以防止再次出现此类情况。
故事的寓意是... 永远不要使用DELETE来删除大量记录,因为它需要将所有已删除的记录存储在重做日志中,速度非常慢。要么复制并切换,要么使用TRUNCATE。
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
上没有索引,则后者将更快。
你可能会想要复制这些行,但请注意,复制2G
行时会产生比删除11M
行时更多的REDO
和UNDO
信息。
dupfield
上没有索引,将会进行排序,这可能需要很长时间。在rowid
上的连接将是HASH SEMI JOIN
,对于11M
行数据,在2G
上只需要几分钟。删除本身也需要数十分钟,主要是为了生成REDO
和UNDO
。 - QuassnoiOracle
足够聪明,可以将其重写为更高效的 HASH SEMI JOIN
。 - Quassnoi 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;
首先,在定义和包含重复值的列上放置索引,
然后,假设表具有主键(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分区的广泛使用,您可以将重复值存储在临时表中,然后在单个事务内删除每对重复项...
假设只有一列(称为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;
未经测试,因此语法可能需要调整...