← 中文资源

简体中文版本

ROW_NUMBER 去重保留最新

用 ROW_NUMBER 按键分区排序标记重复行:首行为主记录其余为重复,DELETE USING 一语句清扫,唯一索引从源头堵漏。

阅读英文原文

示例说明

重复数据的治理分三步:识别、清扫、堵漏。ROW_NUMBER 按邮箱分区、按时间倒序——序号为一的就是该邮箱的最新记录;排序键里补上 id 保证同秒创建的记录也有确定序。DELETE USING 的联结删除让一条语句清完全部重复;CONCURRENTLY 建唯一索引不锁表,从写入端根治复发。

技术要点

  • 分区排序让每组首行可辨
  • DELETE USING 单语句联结删除
  • 唯一索引 CONCURRENTLY 在线创建

代码解读

第一个查询输出全部行并标注 is_primary——清扫前先审计重复规模与形态;DELETE 语句复用同一排名逻辑删除序号大于一的行;最后唯一索引让重复插入直接失败,配合应用层的 ON CONFLICT 处理决定冲突语义。三个步骤形成完整的数据质量闭环。

使用建议

清扫前备份并审计外键引用,被删行可能挂在其他表上。唯一索引上线前先解决存量重复。软删除场景用部分唯一索引(WHERE deleted_at IS NULL)。

代码示例

-- 每个邮箱保留 created_at 最新的一行,标记其余为重复
WITH ranked AS (
  SELECT
    id,
    email,
    created_at,
    ROW_NUMBER() OVER (
      PARTITION BY email
      ORDER BY created_at DESC, id DESC
    ) AS rn
  FROM subscribers
)
SELECT id, email, created_at, (rn = 1) AS is_primary
FROM ranked
ORDER BY email, rn;

-- 一次性清扫:删除全部非首行
WITH ranked AS (
  SELECT id,
         ROW_NUMBER() OVER (
           PARTITION BY email
           ORDER BY created_at DESC, id DESC
         ) AS rn
  FROM subscribers
)
DELETE FROM subscribers s
USING ranked r
WHERE s.id = r.id
  AND r.rn > 1;

-- 上游堵漏:唯一索引阻止重复再次写入
CREATE UNIQUE INDEX CONCURRENTLY idx_subscribers_email
  ON subscribers (email);