如何在SQL中不使用GROUP BY通过CASE表达式求和(求最优方案)
让我来帮你修正这个查询并给出更优方案——你的原写法逻辑上有问题,因为把CASE和SUM的嵌套顺序搞反了,而且不需要GROUP BY就能实现你的需求。
先说说原查询的问题
你的原查询写法存在逻辑缺陷:你在CASE表达式里嵌套了SUM聚合函数,但没有GROUP BY的情况下,SUM是对全表的cost进行计算,而CASE的判断是基于某一行的lab_date(数据库会默认返回任意一行的lab_date,或者直接报错,取决于你使用的数据库),这会导致结果完全不符合你按日期区间计算tax的预期。
正确的实现方案(无需GROUP BY)
如果你想要计算整个表中符合条件的所有行的tax总和,把CASE放到SUM里面做条件聚合是最标准的写法:
SELECT SUM( CASE -- 2018年初到3月24日,按cost的5%计算tax WHEN lab_date BETWEEN '2018-01-01' AND '2018-03-24' THEN ISNULL(cost, 0) * 0.05 -- 3月25日及以后,按cost的95%计算tax(即cost减去5%) WHEN lab_date >= '2018-03-25' THEN ISNULL(cost, 0) * 0.95 -- 处理不在这两个区间的日期,贡献0 ELSE 0 END ) AS total_tax FROM LABOR;
这个逻辑是逐行判断每一行的lab_date属于哪个区间,计算该行对应的tax贡献值,最后用SUM把所有行的贡献加起来。整个查询不需要GROUP BY,就能得到全局的总tax,而且逻辑清晰,数据库也能高效执行。
如果需要分别查看两个区间的tax总和
要是你想分开得到两个时间段的tax数值,同样不用GROUP BY,只需要写两个条件聚合即可:
SELECT SUM(CASE WHEN lab_date BETWEEN '2018-01-01' AND '2018-03-24' THEN ISNULL(cost, 0)*0.05 ELSE 0 END) AS tax_early_period, SUM(CASE WHEN lab_date >= '2018-03-25' THEN ISNULL(cost, 0)*0.95 ELSE 0 END) AS tax_late_period FROM LABOR;
总结最优方案
条件聚合(SUM/AVG等聚合函数内嵌套CASE)是这类“按不同条件计算聚合值”需求的标准最优方案,相比你原有的写法,它:
- 逻辑更直观,避免了聚合函数和CASE嵌套顺序错误的问题
- 不需要GROUP BY就能实现全局聚合
- 性能更优,数据库可以直接通过一次扫描完成计算
内容的提问来源于stack exchange,提问作者Sam Bin Ham
相关产品推荐
相关产品推荐

