使用脚本从 SQL Server 表中删除重复行

原始产品版本: SQL Server
原始 KB 数: 70956

总结

本文介绍如何使用 Transact-SQL (T-SQL) 脚本从Microsoft SQL Server的表中删除重复行。 它描述了两种常见方法:将每个重复行的一个实例复制到单独的表中,并使用 ROW_NUMBER 函数删除就地的额外行。 如果表包含重复记录,请参阅本文,例如,因为它没有唯一约束或主键。 方法 2 更简单,因为它是原地进行的。

创建包含重复行的示例表

若要演示这两种方法,请创建一个名为 original_table 的示例表,该表具有一列 key_value,并添加重复数据:

CREATE TABLE original_table (key_value int )

INSERT INTO original_table values (1)
INSERT INTO original_table values (1)
INSERT INTO original_table values (1)

INSERT INTO original_table values (2)
INSERT INTO original_table values (2)
INSERT INTO original_table values (2)
INSERT INTO original_table values (2)

然后,使用以下方法之一从表中删除重复行。

Note

这些脚本永久删除数据。 在针对生产数据执行这些操作之前,请先备份表或数据库。

方法 1:将重复行移动到单独的表并重新插入一个副本

方法 1 使用两个表:original_table 和脚本创建后又删除的第二个表 duplicate_table。 若要将脚本应用于另一个表,请将 original_table 替换为该表的名称。 如果重复项由多个列定义,则此方法需要重写 GROUP BY 和 COUNT 逻辑。

运行以下脚本:

SELECT DISTINCT *
INTO duplicate_table
FROM original_table
GROUP BY key_value
HAVING COUNT(key_value) > 1

DELETE original_table
WHERE key_value
IN (SELECT key_value
FROM duplicate_table)

INSERT original_table
SELECT *
FROM duplicate_table

DROP TABLE duplicate_table

此脚本按给定顺序执行以下操作:

  • 将原始表中任何重复项行的一个实例移动到重复项表。
  • 从原始表中删除所有同样位于重复表中的行。
  • 将重复表中的行移回原始表。
  • 删除 duplicate 表。

此方法很简单。 但是,它要求数据库中有足够的可用空间来临时生成重复表。 此方法也会产生开销,因为要移动数据。

此外,如果表具有 IDENTITY 列,则必须在将数据还原到原始表时使用 SET IDENTITY_INSERT ON 。

方法 2:使用 ROW_NUMBER 函数删除重复行

Microsoft SQL Server 2005 年引入的 ROW_NUMBER 函数使此操作更简单。 将 key_value 替换为定义重复项的列或列。

DELETE T
FROM
(
SELECT *
, DupRank = ROW_NUMBER() OVER (
              PARTITION BY key_value
              ORDER BY (SELECT NULL)
            )
FROM original_table
) AS T
WHERE DupRank > 1 

此脚本按给定顺序执行以下操作:

  • 使用 ROW_NUMBER 函数根据 key_value 对数据进行分区,其中 key_value 可以是一个或多个用逗号分隔的列。
  • 删除所有其 DupRank 值大于 1 的记录。 此值表示记录是重复项。

由于该 (SELECT NULL) 表达式,脚本不会根据任何条件对分区数据进行排序。 如果删除重复项的逻辑需要根据其他列的排序顺序决定删除哪些记录以及保留哪些记录,则可以使用 ORDER BY 表达式执行此操作。

删除重复项时保留最新行

若要保留具有最新值的行,请按该列降序排序。 例如,假设某个假设的 employee_table 对同一员工有多行记录。 以下脚本仅保留具有最新 updated_date 值的行:

DELETE T
FROM
(
SELECT *
, DupRank = ROW_NUMBER() OVER (
              PARTITION BY employee_name
              ORDER BY updated_date DESC
            )
FROM employee_table
) AS T
WHERE DupRank > 1

具有最新 updated_date 值的行其 DupRank 值为 1,因此脚本将保留该行并删除较旧的行。

方法 2 简单且有效,原因如下:

  • 它不需要您暂时将重复记录复制到另一个表。
  • 它不需要将原始表与自身联接(例如,通过使用使用组合 GROUP BY 和 HAVING返回所有重复记录的子查询)。
  • 为了获得最佳性能,应在该表上建立相应的索引,使用 key_value 作为索引键,并包含你可能在 ORDER BY 表达式中使用的任何排序列。

该ROW_NUMBER函数可用于 SQL Server 2005 及更高版本。 如果使用的 SQL Server 版本不支持 ROW_NUMBER 函数,请改用方法 1 或类似的方法。

如何防止表中出现重复行?

删除重复项后,可以通过对用于标识一行的一个或多个列定义 唯一约束 或 唯一索引 来帮助防止出现新的重复行。