SQL内连接无匹配记录求和返回0及CASE结果异常排查
问题解答
1. ISNULL是否需要替换为COALESCE
不需要。二者都能把计算结果中的NULL值转为0,但你当前查询缺失无匹配月份的0值,和用哪个空值处理函数无关——你用了INNER JOIN关联两张表,当B_ENTRYLOSS中没有对应分类、对应月份的记录时,整行会被内连接直接过滤,根本不会进入后续SUM、空值转换的计算流程,自然不会返回你要的0值行。
2. 两种CASE写法结果差异原因
你写的多分支CASE逻辑本身是错误的:CASE表达式是按书写顺序逐行判断,只要命中第一个满足条件的分支,就会直接返回对应结果,不再执行后续判断。
举个实际例子:如果某条记录的DFROM值是WEAVING,第一个判断条件DFROM != 'SEWING'就已经成立,会直接统计该行QTY,根本不会走到后面判断DFROM是否等于WEAVING的分支。你这套多分支写法的实际效果等价于「只要DFROM不是SEWING,就全部计入TOTAL_LOSS_SEMUA」,和你要排除列表内7个值的需求完全不符,结果自然偏差极大。
你之前写的NOT IN单分支写法逻辑是正确的,只要注意如果DFROM字段存在NULL值,NOT IN会返回UNKNOWN导致对应行不被统计,如果业务上DFROM不可能为NULL可以直接使用。
修正后实现方案
要实现「每个分类覆盖2022-04到2022-06全部月份,无匹配记录时统计值返回0」的效果,需要先生成「所有分类+所有目标统计月份」的基础笛卡尔积表,再左关联损失记录表做聚合,修正后的SQL如下:
-- 构造要统计的目标月份列表 WITH MonthList AS ( SELECT '2022-04' AS CURR_MONTH UNION ALL SELECT '2022-05' UNION ALL SELECT '2022-06' ), -- 生成分类+月份的全量基础行,保证每个分类每个月都有对应记录 BaseDimension AS ( SELECT TCS.departement, TCS.category, ML.CURR_MONTH FROM TBL_CATEGORY TCS CROSS JOIN MonthList ML ) SELECT BD.departement, BD.category, BD.CURR_MONTH, ISNULL(SUM(CASE WHEN BE.DFROM IN('SEWING','WEAVING','FORMING','DYE','HSM','INSPECTION','WOVEN') AND BE.DFROM = BE.DTO THEN BE.QTY END), 0) AS TOTAL_LOSS_PROSES, ISNULL(SUM(CASE WHEN BE.DFROM NOT IN('SEWING','WEAVING','FORMING','DYE','HSM','INSPECTION','WOVEN') THEN BE.QTY ELSE 0 END), 0) AS TOTAL_LOSS_SEMUA FROM BaseDimension BD -- 左关联事实表,无匹配记录时保留基础行 LEFT JOIN B_ENTRYLOSS BE ON BD.category = BE.LOSSCAT AND FORMAT(BE.DT, 'yyyy-MM') = BD.CURR_MONTH GROUP BY BD.departement, BD.category, BD.CURR_MONTH ORDER BY BD.category ASC, BD.CURR_MONTH DESC;
注意点
- 事实表B_ENTRYLOSS的时间过滤条件必须写在LEFT JOIN的ON子句中,不能写在WHERE里,否则左关联生成的NULL行会被WHERE条件过滤,失去补0的效果。
- 如果后续需要统计的月份范围更长,不需要硬写UNION生成月份列表,可以用递归CTE或者系统日期维表生成连续月份,扩展性更好。
内容的提问来源于stack exchange,提问作者user17697342
相关产品推荐
相关产品推荐

