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

PostgreSQL中合并相同item_id的行并删除重复行的单事务实现方案咨询

PostgreSQL中合并相同item_id的行并删除重复行的单事务实现方案咨询

嗨,我来帮你梳理下这个问题~你现在需要处理的是单表内item_id重复的行,要把相同item_id的qty求和后保留一行、删除其他行,还希望所有操作在单个事务里完成,对吧?

先明确你的场景:

  • 表结构包含id、item_id、qty三列
  • 目标:对每个item_id求和qty,更新其中一行的数量为总和,删除同item_id的其他行
  • 需求:所有操作原子性完成(单事务),你试过MERGE但没找到合适的用法,还考虑过封装成过程/函数

原始数据集:
原始数据集

你之前参考的分步操作大概是这样的(来自外部方案):

UPDATE test t
SET qty = s.qty
...

直接的单事务实现方案

其实不用复杂的存储过程,用PostgreSQL的CTE(公共表表达式)配合事务块就能搞定,所有操作都在一个事务里,确保要么全部成功要么全部回滚。核心思路是先算每个item_id的总数量,更新到你想保留的那一行(比如保留id最小的行),再删除其他重复行:

BEGIN;

-- 第一步:计算每个item_id的总qty,更新到对应保留行(这里选id最小的行)
WITH item_totals AS (
    SELECT item_id, SUM(qty) AS total_qty
    FROM test
    GROUP BY item_id
)
UPDATE test t
SET qty = it.total_qty
FROM item_totals it
WHERE t.item_id = it.item_id
AND t.id = (SELECT MIN(id) FROM test WHERE item_id = it.item_id);

-- 第二步:删除每个item_id中除保留行外的其他行
DELETE FROM test t
WHERE EXISTS (
    SELECT 1
    FROM test t2
    WHERE t2.item_id = t.item_id
    AND t2.id < t.id -- 这里的逻辑是保留id最小的行,如果你想保留最大id的话改成t2.id > t.id即可
);

COMMIT;

关于MERGE的小说明

你提到的MERGE语法确实更适合跨表的合并场景(比如把A表的数据合并到B表,做更新或插入),单表内的这种合并删除用上面的方式会更直接,没必要硬套MERGE。

可选:封装成存储过程(方便复用)

如果这个逻辑需要重复执行,把它封装成存储过程会更方便,调用起来更简单,而且过程里的操作默认也是在一个事务里执行的:

CREATE OR REPLACE FUNCTION merge_duplicate_items()
RETURNS VOID AS $$
BEGIN
    WITH item_totals AS (
        SELECT item_id, SUM(qty) AS total_qty
        FROM test
        GROUP BY item_id
    )
    UPDATE test t
    SET qty = it.total_qty
    FROM item_totals it
    WHERE t.item_id = it.item_id
    AND t.id = (SELECT MIN(id) FROM test WHERE item_id = it.item_id);

    DELETE FROM test t
    WHERE EXISTS (
        SELECT 1
        FROM test t2
        WHERE t2.item_id = t.item_id
        AND t2.id < t.id
    );
END;
$$ LANGUAGE plpgsql;

调用的时候只需要执行SELECT merge_duplicate_items();就可以啦。

备注:内容来源于stack exchange,提问作者cxp cxd

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.17 10:52:59