协慌网

登录 贡献 社区

如何删除重复的行?

从相当大的SQL Server表(即 300,000 多行)中删除重复行的最佳方法是什么?

当然,由于RowID标识字段的存在,行不会是完美的重复。

MyTable 的

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null

答案

假设没有空值,您将GROUP BY唯一列,并SELECT MIN (or MAX) RowId 作为要保留的行。然后,只删除没有行 id 的所有内容:

DELETE FROM MyTable
LEFT OUTER JOIN (
   SELECT MIN(RowId) as RowId, Col1, Col2, Col3 
   FROM MyTable 
   GROUP BY Col1, Col2, Col3
) as KeepRows ON
   MyTable.RowId = KeepRows.RowId
WHERE
   KeepRows.RowId IS NULL

如果您有 GUID 而不是整数,则可以替换

MIN(RowId)

CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn)))

另一种可行的方法是

; 

--Ensure that any immediately preceding statement is terminated with a semicolon above
WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY Col1, Col2, Col3 
                                       ORDER BY ( SELECT 0)) RN
         FROM   #MyTable)
DELETE FROM cte
WHERE  RN > 1;

我正在使用上面的ORDER BY (SELECT 0) ,因为它是任意哪一行在出现平局时保留。

例如,要保留RowID顺序中的最新版本,可以使用ORDER BY RowID DESC

执行计划

对于此,执行计划通常比接受的答案更简单,更有效,因为它不需要自联接。

执行计划

但情况并非总是如此。可能首选GROUP BY解决方案的地方是优先选择散列聚合而不是流聚合的情况。

ROW_NUMBER解决方案将始终提供相同的计划,而GROUP BY策略更灵活。

执行计划

可能有利于散列聚合方法的因素是

  • 分区列上没有有用的索引
  • 相对较少的群体,每组中具有相对较多的重复

在第二种情况的极端版本中(如果每个组中有很多重复的组很少),也可以考虑简单地插入行以保留到新表中然后TRUNCATE -ing 原始并将其复制回以最小化与删除相比的日志记录行的比例很高。

有一篇关于删除 Microsoft 支持站点上的重复项的文章。这是相当保守的 - 他们让你在不同的步骤中做所有事情 - 但它应该适用于大表。

我过去曾经使用过自联接来做这个,虽然它可能会被 HAVING 子句搞定:

DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField