SQL中使用GROUP BY与DISTINCT计算列SUM()结果异常问题
问题描述
我编写了如下查询语句:
select distinct ProdQty, JobCompletionDate, JobHead.JobNum from erp.JobHead inner join erp.LaborDtl on JobHead.JobNum = LaborDtl.JobNum and JobHead.Company = LaborDtl.Company where JobCompletionDate = '2022-01-04' and JobHead.Company = 'TD' and LaborDtl.JCDept ='MS'
该语句可返回指定JobCompletionDate下,每个JobNum对应的ProdQty值,以下是返回结果的片段:
| Prod Qty | JobCompletionDate | JobNum |
|---|---|---|
| 12 | 2022-01-04 | 198583 |
| 1 | 2022-01-04 | 205388 |
| 2 | 2022-01-04 | 205562 |
此处未粘贴完整结果表,核心逻辑清晰:我在查询中使用distinct是因为ProdQty通常存在重复条目,该关键字可剔除重复记录。
接下来我需要按JobCompletionDate执行GROUP BY分组,统计对应日期下ProdQty的总和,但对ProdQty字段使用SUM()时遇到问题:我通过Excel或计算器手动累加该列数值,得到正确结果为7201,但执行如下查询时:
select sum(distinct ProdQty) from erp.JobHead inner join erp.LaborDtl on JobHead.JobNum = LaborDtl.JobNum and JobHead.Company = LaborDtl.Company where JobCompletionDate = '2022-01-04' and JobHead.Company = 'TD' and LaborDtl.JCDept ='MS' group by JobCompletionDate
返回的结果为6660,与正确值不符。请问该问题产生的原因是什么?我的SQL写法存在什么错误?
问题原因
- 核心错误是误用了
SUM(DISTINCT 字段)语法:该语法的执行逻辑是对结果集中的ProdQty值做全局去重,相同数值仅保留1次参与求和,而非你预期的「先剔除重复的整行记录,再对保留行的ProdQty求和」。 - 举个最直观的例子:如果两个不同JobNum的工单ProdQty都是12,
SUM(DISTINCT ProdQty)只会把这个12计算1次,而你手动累加时两个工单的12会合计为24,结果必然偏小。你得到的6660比正确值少541,就是所有重复出现的ProdQty值被少算的总和。 - 你最初写
SELECT DISTINCT ProdQty, JobCompletionDate, JobHead.JobNum时,去重判断依据是三个字段的组合值:只要JobNum不同,哪怕ProdQty、JobCompletionDate完全一致,也会被判定为不同记录保留,这才是你需要的去重效果——你本来要剔除的是联表时一对多关系带出来的同一个JobNum的重复行,不是把生产数量相同的不同工单合并计算。
正确写法
不要在SUM函数内加DISTINCT,先完成行级去重再做聚合,两种常用写法都可以得到正确结果:
- 基于你最初的去重逻辑套一层子查询求和
select sum(ProdQty) from ( select distinct ProdQty, JobCompletionDate, JobHead.JobNum from erp.JobHead inner join erp.LaborDtl on JobHead.JobNum = LaborDtl.JobNum and JobHead.Company = LaborDtl.Company where JobCompletionDate = '2022-01-04' and JobHead.Company = 'TD' and LaborDtl.JCDept ='MS' ) t group by JobCompletionDate
- 先按工单维度聚合去重再求和(大表场景下性能更优)
select sum(ProdQty) from ( select max(ProdQty) as ProdQty, JobCompletionDate from erp.JobHead inner join erp.LaborDtl on JobHead.JobNum = LaborDtl.JobNum and JobHead.Company = LaborDtl.Company where JobCompletionDate = '2022-01-04' and JobHead.Company = 'TD' and LaborDtl.JCDept ='MS' group by JobHead.JobNum, JobCompletionDate ) t group by JobCompletionDate
额外说明:你一开始需要加DISTINCT的根本原因,是LaborDtl表中同一个JobNum可能对应多条符合
JCDept='MS'的记录,联表时会把JobHead的单条工单记录复制多份。如果业务上能保证每个符合筛选条件的JobNum在LaborDtl中仅对应1条记录,从根源上避免一对多联表产生的重复行,就不需要额外加去重逻辑。
内容的提问来源于stack exchange,提问作者Avery1
相关产品推荐
相关产品推荐

