在数据库管理和数据清洗的过程中,经常会遇到数据重复的问题,在 OrderInfo 表中,同一个 ProductID 可能因为录入错误出现了两次,对于 SQL Server 而言,如果需要从多条完全相同或部分相同的记录中只保留一条,删除其余的,我们需要使用一些高效的技巧。
本文将介绍几种在 SQL Server 中处理“两条同样的信息删一条”的常用方法,其中最推荐使用的是基于 窗口函数 (ROW_NUMBER()) 的方法。
场景假设
假设我们有一张表名为 Users,其中包含用户的姓名和邮箱,有些用户因为网络原因出现了两条完全一样的记录(Name 和 Email 都相同)。

| ID | Name | |
|---|---|---|
| 1 | 张三 | zhangsan@test.com |
| 2 | 李四 | lisi@test.com |
| 3 | 张三 | zhangsan@test.com |
| 4 | 王五 | wangwu@test.com |
目标:删除 ID 为 3 的记录,只保留 ID 为 1 的记录。
使用 ROW_NUMBER() 窗口函数(推荐)
这是目前 SQL Server 中最标准、最灵活的处理方式,通过 ROW_NUMBER(),我们可以给重复的数据打上序号,然后只删除序号大于 1 的记录。
编写删除语句
我们需要先使用 WITH (公用表表达式) 来生成带有行号的临时结果集,然后执行删除操作。
WITH CTE_Deduplication AS (
-- 1. 生成行号:按 Name 和 Email 分组,如果相同则分配序号
SELECT
ID,
Name,
Email,
-- 2. 使用 ROW_NUMBER() 窗口函数
-- PARTITION BY 后面跟分组条件(这里是 Name 和 Email)
-- ORDER BY 后面跟排序条件(通常是 ID,保证保留最新的或最小的那条)
ROW_NUMBER() OVER(PARTITION BY Name, Email ORDER BY ID) AS RowNum
FROM
Users
)
-- 3. 删除行号大于 1 的记录
DELETE FROM CTE_Deduplication
WHERE RowNum > 1;
代码解析:
PARTITION BY Name, Email:告诉 SQL Server 哪些数据是“同样的”。ORDER BY ID:决定当有多条重复数据时,保留哪一条(ID 小的保留,ID 大的被删)。WHERE RowNum > 1:只删除标记为重复的行。
使用临时表(适用于旧版本或特定场景)
如果你使用的 SQL Server 版本较旧,不支持窗口函数,或者需要更复杂的逻辑,可以使用临时表的方法。
创建临时表并插入唯一数据
-- 创建一个临时表,只保留不重复的数据 SELECT ID, Name, Email INTO #TempUsers FROM Users GROUP BY ID, Name, Email HAVING COUNT(*) = 1;
删除原表数据并恢复
-- 删除原表数据 TRUNCATE TABLE Users; -- 将临时表的数据插回原表 INSERT INTO Users (ID, Name, Email) SELECT ID, Name, Email FROM #TempUsers; -- 删除临时表 DROP TABLE #TempUsers;
重要注意事项
在执行删除操作之前,请务必注意以下几点,以防止数据丢失:
- 备份数据:在生产环境操作前,请先对表进行
BACKUP备份,或者使用SELECT * INTO ...将数据导出到新表中验证无误后再操作。 - 事务保护:使用
BEGIN TRANSACTION和COMMIT包裹你的删除语句,如果执行后发现问题,可以轻松执行ROLLBACK恢复数据。BEGIN TRANSACTION; -- 执行删除代码 -- IF @@ROWCOUNT > 0 PRINT '删除成功'; -- ROLLBACK; -- 如果出问题,取消操作 COMMIT;
- 索引:确保你的分组列(如
Name和Email)上有索引,这会显著提高PARTITION BY和GROUP BY的查询速度。
对于 SQL Server 中“两条同样的信息删一条”的需求,使用 ROW_NUMBER() 窗口函数 是最佳实践,它逻辑清晰、性能较好,并且能够精确控制保留哪一条记录(例如保留 ID 最小的,或者创建时间最新的)。
