SQL如何按CTC、月份实现两组GROUP BY聚合的SUM差值计算
实现结论
完全可以通过单条SQL实现,不需要拆分多次单年查询再做关联匹配,用条件聚合的方式仅需一次表扫描就能完成计算,查询效率更高。
可直接使用的MySQL适配SQL
$query = " SELECT CTC, MONTH(date) AS monthVal, -- 计算逻辑:同一CTC、月份维度下,2020年聚合值减2019年聚合值 SUM(IF(YEAR(date) = 2020, totalVal, 0)) - SUM(IF(YEAR(date) = 2019, totalVal, 0)) AS valDiff FROM $table WHERE -- 限定扫描范围为2019、2020两年,减少无效数据处理 YEAR(date) IN (2019, 2020) -- 保持和原单年查询完全一致的过滤条件,避免统计口径偏差 CTC2 = 'Sup1' GROUP BY CTC, MONTH(date) ";
核心逻辑说明
- 条件聚合的思路可以避免拆分两次单年分组查询再做LEFT JOIN的写法,减少一次全表扫描的开销。分组过程中通过IF判断把不同年份的totalVal归入对应的求和分支,直接在分组阶段算出两年的聚合值,再做差值计算。
- 如果需要严格只保留两年都存在数据的CTC+月份组合(排除仅单年有数据的无效行),可以在查询末尾追加
HAVING SUM(IF(YEAR(date) = 2020, 1, 0)) > 0 AND SUM(IF(YEAR(date) = 2019, 1, 0)) > 0。
其他数据库适配
如果使用SQL Server、PostgreSQL等不支持MySQL专属IF函数的数据库,只需要把条件判断替换为标准CASE WHEN语法,核心计算逻辑完全一致:
SUM(CASE WHEN YEAR(date) = 2020 THEN totalVal ELSE 0 END) - SUM(CASE WHEN YEAR(date) = 2019 THEN totalVal ELSE 0 END) AS valDiff
输出格式调整
拿到SQL返回的CTC、monthVal、valDiff三列结果后,在PHP代码层遍历结果集,用分号拼接每行的三个字段,即可生成你需要的Mr1;1;-150格式的输出,不建议在SQL层面做字符串拼接,避免数据查询和展示逻辑耦合。
注意:
- 所有过滤条件必须保持两年计算口径一致,比如原单年查询是过滤
CTC2='Sup1'的,就不能在2019年的聚合计算中漏掉这个条件,否则会出现差值计算错误。- 字符串匹配的大小写要和实际表存储的值完全一致,避免因为大小写不匹配导致过滤失效。
内容的提问来源于stack exchange,提问作者Matts
相关产品推荐
相关产品推荐

