COUNT(*)和COUNT(col)返回0,因为它们统计行存在性,空集或全NULL时语义明确;SUM(col)返回NULL,因其计算数值代数和,空集在SQL标准中无定义。

因为 COUNT 统计的是“行的存在性”,而 SUM 计算的是“数值的代数和”——前者天然支持“零个”的语义,后者在数学上对空集无定义,SQL 标准强制返回 NULL。
为什么 COUNT(*) 和 COUNT(col) 都返回 0,而 SUM(col) 返回 NULL
COUNT 是唯一把“空集”当作合法输入并返回确定值(0)的聚合函数。它不依赖列值内容,只问“有没有行”。哪怕 WHERE 1=0 或表为空,COUNT(*) 仍返回 0。
SUM、AVG、MAX、MIN 都需要至少一个非 NULL 值参与运算;空集时无法构造有效结果,只能返回 NULL 表示“未定义”。这不是 bug,是 SQL 标准行为。
-
COUNT(*):统计所有行,含NULL,空集 →0 -
COUNT(col):统计该列非NULL的行数,空集或全NULL→0 -
SUM(col):对非NULL值求和,空集或全NULL→NULL -
AVG(col):等价于SUM(col)/COUNT(col),空集 →NULL
GROUP BY 下 COUNT 返回 NULL?那是你加了 GROUP BY 却没数据
当带 GROUP BY 且某组完全无匹配行时,该组不会出现在结果中——所以你根本看不到那行。但如果你用 LEFT JOIN 强制保留分组维度(比如城市列表),再对右表聚合,这时 COUNT 会返回 0,而 SUM 仍为 NULL。
典型翻车场景:SELECT city, COUNT(*), SUM(sales) FROM cities LEFT JOIN orders ON cities.id = orders.city_id GROUP BY cities.id —— 若某城市无订单,COUNT(*) 是 0,SUM(sales) 是 NULL。
- 别指望
GROUP BY自动补出“零值行”,必须靠LEFT JOIN或UNION构造主维度 -
COUNT(*)在这种LEFT JOIN场景下返回0,是因为左表有行,右表无匹配 → 行存在,只是右表字段全NULL -
SUM(col)对全NULL列求和,仍返回NULL,不是0
怎么让 SUM(NULL 或空集) 返回 0:COALESCE 还是 IFNULL?
统一用 COALESCE(SUM(col), 0)。它在所有主流数据库(MySQL、PostgreSQL、SQL Server、Oracle)都可用,语义清晰:先聚合,再兜底。
避免写 SUM(COALESCE(col, 0))——这会把原数据中的 NULL 先转成 0 再加总,逻辑不同:前者是“整组无数据才给 0”,后者是“每个 NULL 当 0 算”。业务含义差很远。
-
COALESCE(SUM(col), 0):空组 / 全NULL组 →0;有数据组 → 实际和 -
IFNULL(SUM(col), 0):MySQL 专用,效果同上,但跨库迁移要改 -
ISNULL(SUM(col), 0):SQL Server 专用,参数顺序反着(ISNULL(表达式, 替代值)) - 别在
WHERE里用COALESCE(col, 0) = 1,索引大概率失效
最容易被忽略的“假零”:整组被 WHERE 干掉了
你以为某类数据 SUM 是 0,其实它压根没出现在结果里。比如 SELECT dept, SUM(salary) FROM emp WHERE status = 'active' GROUP BY dept —— 如果某个部门全员 inactive,它不会显示 dept, NULL 或 dept, 0,而是直接消失。
真要看到“零值部门”,得把条件内移到聚合里:SUM(CASE WHEN status = 'active' THEN salary ELSE 0 END),同时去掉外部 WHERE。
- 结果里“缺行” ≠ “值为 0” ≠ “值为 NULL”,三者需分开诊断
- 先
EXPLAIN或加COUNT(*)看该组是否存在,再决定用COALESCE还是重构WHERE逻辑 - 财务报表类需求,通常要求“每类必现”,必须用
LEFT JOIN + COALESCE(SUM(), 0)组合











