从相当大的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 原始并将其复制回以最小化与删除相比的日志记录行的比例很高。