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

MySQL/SQL中SUM聚合求和时如何排除重复行得到正确结果

交易表重复数据导致金额求和偏差修复方案

问题核心逻辑

  • 重复数据判定规则:同一cashier_id(收银员ID)下,folio(单据号)相同的记录属于重复行,统计时仅需计算1次
  • 原查询缺陷:直接对全表数据做聚合,重复行的金额被多次累加,导致求和结果、笔数统计值偏大;同时原SQL存在语法错误:cash_amount聚合行末尾多了1个多余逗号,执行会触发语法报错
  • 示例偏差说明:测试数据中cashier_id=1下folio=0001(金额2500)重复2次,原查询统计结果为6000,去重后正确结果应为3500;cashier_id=2无重复数据,统计值10000为正确结果

可用修复写法

写法1:通用兼容写法(适配所有SQL版本)

先通过DISTINCT按「收银员+单据号」维度完成去重,再基于去重后的结果集做分组聚合,逻辑简单兼容性强,适合绝大多数场景:

SELECT 
    t.cashier_id AS cashier_id,
    SUM(t.cash_amount) AS cash_amount,
    COUNT(0) AS ticket_number,
    t.date AS date
FROM (
    -- 子查询先完成去重,重复单据仅保留1条有效记录
    SELECT DISTINCT
        cashier_id,
        folio,
        cash_amount,
        DATE(created_at) AS date
    FROM `jysparki_jis`.`api_transactions`
    WHERE 
        DATE(created_at) >= '2022-01-01'     
        AND dte_type_id IN (39, 61)    
        AND cashier_id <> 0
) t
GROUP BY t.cashier_id, t.date

写法2:窗口函数写法(支持MySQL8.0+、PostgreSQL等支持窗口函数的环境)

如果重复行存在内容差异(比如重复上传的单据有状态、更新时间区别),可以用ROW_NUMBER()自定义保留规则,比如保留最新创建的记录:

SELECT 
    cashier_id,
    SUM(cash_amount) AS cash_amount,
    COUNT(0) AS ticket_number,
    DATE(created_at) AS date
FROM (
    SELECT 
        *,
        -- 按收银员+单据号分组,按创建时间倒序,最新的记录排第1位
        ROW_NUMBER() OVER (PARTITION BY cashier_id, folio ORDER BY created_at DESC) AS rn
    FROM `jysparki_jis`.`api_transactions`
    WHERE 
        DATE(created_at) >= '2022-01-01'     
        AND dte_type_id IN (39, 61)    
        AND cashier_id <> 0
) t
WHERE rn = 1 -- 仅保留每个分组下排序第1的有效记录
GROUP BY cashier_id, DATE(created_at)

避坑说明

  • 禁止直接使用SUM(DISTINCT cash_amount)实现去重求和:如果同一收银员下存在两个不同单据金额恰好相等,会被误判为重复数据剔除,导致求和结果偏小
  • 单据笔数统计COUNT(0)必须放在去重逻辑之后计算,否则会把重复行统计为多笔交易,导致笔数指标失真
  • 过滤条件中dte_type_id的多值判断用IN (39,61)写法替代多个OR判断,可读性更强、执行效率一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 06:15:35