SQL没有“更新重复数据并保留一条”的原子操作,本质是删除冗余行;关键在于通过ROW_NUMBER()或JOIN+子查询确定保留哪条记录,并依赖时间、ID等字段确保排序稳定性。

直接说结论:SQL 本身没有「更新重复数据并保留一条」的原子操作;所谓“更新”,实际是「先删后留」或「用新值覆盖旧值」,核心动作仍是删除冗余行 + 保证业务逻辑正确性。关键在怎么选那条该留下的记录。
为什么不能直接 UPDATE 重复行?
UPDATE 是逐行修改,不是去重操作。你没法对“所有 name='张三' 的行”统一执行一条 UPDATE 就让它们变成一条——数据库会报错或批量改出一堆相同新记录,反而更乱。
常见错误现象:ERROR 1093 (HY000): You can't specify target table 't' for update in FROM clause(MySQL),本质是试图在 UPDATE 的子查询里读同一张表。
- UPDATE 不解决重复问题,只改变已有行的值
- 真正要的是“收缩行数”,必须靠 DELETE 或 INSERT+TRUNCATE 实现
- 所谓“保留一条”,本质是定义“哪一条算‘该留’的”——靠时间字段、ID 大小、业务权重等
SQL Server:用 ROW_NUMBER() 删除旧重复行
这是最稳妥、可读性强、支持多字段去重的方式。重点在 ORDER BY 子句决定谁留下。
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY name, phone
ORDER BY update_time DESC, id DESC
) AS rn
FROM users
)
DELETE FROM cte WHERE rn > 1;
说明:
-
PARTITION BY name, phone定义重复判断维度(可单列或多列) -
ORDER BY update_time DESC, id DESC确保最新更新的那条排第一;如果update_time可能重复,加id DESC防止排序不稳定 - 别用
ORDER BY (SELECT 0)—— 它不保证顺序,留哪条纯看 luck - 执行前务必先查:把
DELETE换成SELECT *,确认rn = 1的行是你想要的
MySQL:绕过 1093 错误的两种实操写法
MySQL 不允许 UPDATE/DELETE 中子查询直接引用目标表,必须“套一层壳”。
推荐用法(LEFT JOIN + 子查询):
DELETE t1 FROM users t1 LEFT JOIN ( SELECT MIN(id) AS keep_id, name, phone FROM users GROUP BY name, phone ) t2 ON t1.name = t2.name AND t1.phone = t2.phone AND t1.id = t2.keep_id WHERE t2.keep_id IS NULL;
说明:
- 子查询
t2找出每组该保留的id(这里选最小 ID,你也可以换成MAX(id)或结合update_time) -
LEFT JOIN把原表和“该留的 ID”关联,WHERE t2.keep_id IS NULL即匹配不到的——就是该删的 - 性能注意:大表务必在
(name, phone)上建联合索引,否则GROUP BY极慢
替代方案(临时表):
CREATE TEMPORARY TABLE tmp_keep AS SELECT MIN(id) AS id FROM users GROUP BY name, phone; DELETE FROM users WHERE id NOT IN (SELECT id FROM tmp_keep); DROP TEMPORARY TABLE tmp_keep;
优点:逻辑清晰;缺点:会锁表较久,且 NOT IN 对 NULL 敏感,确保 id 非空。
保留最新一条时,update_time 相同怎么办?
这是最容易被忽略的坑。如果业务上允许 update_time 完全一致(比如批量导入、毫秒级时间戳缺失),仅靠它排序会导致 ROW_NUMBER() 分配不可预测,可能删错。
安全做法是叠加唯一确定性字段:
- 加
id DESC(假设 ID 自增):ORDER BY update_time DESC, id DESC - 加
ROW_NUMBER() OVER (...) % 1000不行——没意义,只是随机 - 真没其他字段可用?先给表加个
created_at DATETIME2(3) DEFAULT GETDATE(),避免裸时间字段做决策
复杂点永远不在语法,而在你怎么定义“最新”——业务语义比 SQL 函数更重要。

















