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

如何将同一发票中相同product_id的行合并为一行并删除其余行?

合并同一发票下相同产品的行(求和quantity和total)

我有如下订单明细表格:

idinvoice_idproduct_idquantitytotal
15102100
2510150
35111200
45111200

我希望将同一发票(invoice_id相同)中拥有相同product_id的行进行合并:把这些行的quantity和total值相加后合并到其中一行(示例里保留了组内最小的id),然后删除其余重复的行。最终输出表格应如下所示:

idinvoice_idproduct_idquantitytotal
15103150
35112400

我原本考虑使用一个SQL函数来返回拥有相同invoice_id和product_id的id列表,再对quantity和总价使用聚合函数。是否存在更简便的实现方式?


当然有更简洁的实现方式!完全不用写复杂的自定义函数,用基础的分组聚合或者窗口函数就能轻松搞定,分两种常用场景给你说明:

场景1:仅需查询合并后的结果(不修改原表)

如果只是想获取合并后的数据集,直接用GROUP BY搭配聚合函数就能实现,还能精准匹配你示例里保留组内最小id的需求:

SELECT 
    MIN(id) AS id,
    invoice_id,
    product_id,
    SUM(quantity) AS quantity,
    SUM(total) AS total
FROM your_table_name
GROUP BY invoice_id, product_id
ORDER BY id;

逻辑解释:

  • GROUP BY invoice_id, product_id:自动把"同发票+同产品"的行归为一组
  • MIN(id):取每组里最小的id作为合并后的行ID(和你的示例输出完全匹配),如果想保留最大ID就换成MAX(id)
  • SUM(quantity)和SUM(total):分别对每组的数量、总价做求和计算

场景2:需要修改原表(删除重复行,保留合并后的数据)

如果要直接修改原表,留下合并后的行并删除冗余行,可以用窗口函数ROW_NUMBER()标记每组的目标行,再进行更新和删除操作:

方式一:用CTE标记并操作

WITH ranked_rows AS (
    SELECT 
        *,
        -- 给每组的行按ID排序,标记序号
        ROW_NUMBER() OVER (
            PARTITION BY invoice_id, product_id 
            ORDER BY id ASC
        ) AS rn,
        -- 提前计算每组的总数量和总价
        SUM(quantity) OVER (PARTITION BY invoice_id, product_id) AS total_qty,
        SUM(total) OVER (PARTITION BY invoice_id, product_id) AS total_amt
    FROM your_table_name
)
-- 先更新组内第一行的数量和总价为求和后的值
UPDATE ranked_rows
SET quantity = total_qty, total = total_amt
WHERE rn = 1;

-- 再删除组内其他冗余行
DELETE FROM ranked_rows
WHERE rn > 1;

方式二:临时表替换法(更兼容部分数据库)

如果你的数据库对CTE的更新/删除支持有限,可以用临时表中转:

-- 1. 将合并结果存入临时表
CREATE TABLE temp_merged AS
SELECT 
    MIN(id) AS id,
    invoice_id,
    product_id,
    SUM(quantity) AS quantity,
    SUM(total) AS total
FROM your_table_name
GROUP BY invoice_id, product_id;

-- 2. 清空原表(注意备份数据!)
TRUNCATE TABLE your_table_name;

-- 3. 把合并后的数据插回原表
INSERT INTO your_table_name (id, invoice_id, product_id, quantity, total)
SELECT * FROM temp_merged;

-- 4. 可选:删除临时表
DROP TABLE temp_merged;

注意事项:

  • 把your_table_name替换成你实际的表名
  • 操作原表前务必备份数据,避免误删
  • 不同数据库(MySQL、PostgreSQL、SQL Server等)在语法细节上略有差异,比如CTE的更新权限、临时表语法,可以根据你的数据库调整

这种原生SQL的方式比自定义函数简单太多,性能也更优,完全能满足你的需求~


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 23:42:41