You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL 12.8父子表重复数据清理方案咨询

解决方案(PostgreSQL 12.8)

针对你的场景,我们可以通过**CTE(公共表表达式)**结合批量更新实现需求,全程无需删除数据,也能高效处理大表。以下是分步实现方案:

1. 标记重复行并生成新ID映射

首先给table_1的重复行分配唯一新ID,同时记录新旧ID的映射关系,用于后续同步table_2:

WITH duplicate_rows AS (
    -- 标记table_1中重复的行,生成唯一行标识、新ID和分组序号
    SELECT 
        ctid,  -- PostgreSQL内置行唯一标识符,无主键/索引时用来精准定位行
        id AS old_id,
        -- 生成自增新ID(按现有ID的数字部分延续,可根据实际规则调整)
        'id-' || (SELECT COALESCE(MAX(SUBSTRING(id FROM '(\d+)')::INT), 3) + ROW_NUMBER() OVER (PARTITION BY id ORDER BY ctid)) AS new_id,
        -- 给每个旧ID的重复行排序,用于后续拆分table_2的关联数据
        ROW_NUMBER() OVER (PARTITION BY id ORDER BY ctid) AS row_num
    FROM table_1
    WHERE id IN (SELECT id FROM table_1 GROUP BY id HAVING COUNT(*) > 1)
),
-- 更新table_1的重复行,并返回映射关系
updated_table1 AS (
    UPDATE table_1
    SET id = dr.new_id
    FROM duplicate_rows dr
    WHERE table_1.ctid = dr.ctid
    RETURNING dr.old_id, dr.new_id, dr.row_num
)
-- 将映射关系存入临时表(大表场景避免内存溢出)
SELECT old_id, new_id, row_num INTO TEMP TABLE id_mapping FROM updated_table1;

2. 同步更新table_2的关联数据

将table_2中每个旧ID的关联数据拆分,一半保留原ID,一半更新为对应新ID:

WITH ranked_table2 AS (
    -- 给每个旧ID的关联行排序,标记总行数和行序号
    SELECT 
        ctid,
        id AS old_id,
        ROW_NUMBER() OVER (PARTITION BY id ORDER BY ctid) AS rn,
        COUNT(*) OVER (PARTITION BY id) AS total_rows
    FROM table_2
    WHERE id IN (SELECT old_id FROM id_mapping)
),
-- 匹配需要更新的行与目标新ID
mapping_match AS (
    SELECT 
        rt2.ctid,
        -- 拆分规则:后半部分行更新为新ID,前半部分保留原ID
        CASE 
            WHEN rt2.rn > rt2.total_rows / 2 THEN im.new_id
            ELSE rt2.old_id
        END AS target_id
    FROM ranked_table2 rt2
    JOIN id_mapping im ON rt2.old_id = im.old_id
    WHERE im.row_num = 2  -- 对应table_1中第二个重复行的新ID
)
-- 批量更新table_2
UPDATE table_2
SET id = mm.target_id
FROM mapping_match mm
WHERE table_2.ctid = mm.ctid;

3. 验证与清理

执行完成后验证数据正确性:

-- 检查table_1是否无重复ID
SELECT id, COUNT(*) FROM table_1 GROUP BY id HAVING COUNT(*) > 1;

-- 检查table_2的ID分布是否符合预期
SELECT id, COUNT(*) FROM table_2 GROUP BY id;

清理临时表:

DROP TABLE id_mapping;

大表优化提示

  • 操作前可临时关闭autovacuum,避免自动清理拖慢性能,完成后再开启
  • 超大规模表可按旧ID分批处理,避免单次更新锁表时间过长
  • 所有操作完成后,重建所需的索引和约束

内容的提问来源于stack exchange,提问作者Julian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 19:40:32