avg()自动跳过null且不会除零报错,但手动实现平均时需用nullif(count(),0)防除零,再用coalesce处理null转0;区分“全null”与“无数据”应结合count和case。

AVG() 本身会自动跳过 NULL,但不会处理除零
SQL 标准里的 AVG() 函数只对非 NULL 值求平均,内部等价于 SUM(col) / COUNT(col)(忽略 NULL)。它**永远不会触发除零错误**,因为 COUNT(col) 在全为 NULL 时返回 0,而多数数据库(如 PostgreSQL、SQL Server)在 SUM()/0 时直接返回 NULL;但 MySQL 是个例外——它默认允许除零并返回 NULL,不过若开启 sql_mode=STRICT_TRANS_TABLES,SUM()/0 会报错。
真正需要你干预的,是「手动实现平均逻辑」的场景,比如:用 SUM(amount) / NULLIF(COUNT(*), 0),或需要把空组显示为 0 而非 NULL。
用 NULLIF 避免除零错误(推荐通用写法)
NULLIF(divisor, 0) 是最干净的防御式写法:当分母为 0 时返回 NULL,整个表达式结果自然为 NULL,不报错。
-
SUM(sales) / NULLIF(COUNT(*), 0)—— 组内无行时,COUNT(*)为0,NULLIF返回NULL,整除结果为NULL - 注意:
COUNT(*)和COUNT(col)不同:前者统计所有行(含NULL),后者只统计非NULL值。若你想模仿AVG(col)的语义(跳过NULL),应写成SUM(col) / NULLIF(COUNT(col), 0) - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持
NULLIF;SQLite 也支持,但注意其COUNT(col)行为与其他库一致
想把 NULL 结果转成 0?COALESCE 要放在外层
别写 COALESCE(AVG(col), 0) 就完事——这看似简单,但掩盖了真实问题:如果整组数据都为 NULL,AVG(col) 返回 NULL,COALESCE 确实能转成 0;但如果组里根本没数据(比如 WHERE false 后只剩空组),某些数据库(如 PostgreSQL)仍会返回一行 NULL,这时 COALESCE 有效;但更常见的是你需要区分「有数据但全 NULL」和「无数据」两种情况。
- 安全做法是:先确保分母不为零(用
NULLIF),再用COALESCE包裹最终结果,例如:COALESCE(SUM(col) / NULLIF(COUNT(col), 0), 0) - 如果业务上「无数据」该显示
0,而「有数据但全NULL」也该显示0,那这行就够了;但若需区分二者,就得加CASE WHEN COUNT(col) = 0 THEN ... END - 别在
AVG()内部套COALESCE(col, 0):这会把原始NULL强制变0,彻底改变平均值语义(比如[1, NULL, 3]变成[1, 0, 3],均值从2变成1.33)
GROUP BY 场景下空组的特殊处理
当你 LEFT JOIN 或带条件聚合时,可能出现某分组完全无匹配行(即 COUNT(*) = 0),此时 AVG(col) 返回 NULL,但你可能希望它参与后续计算(比如除以总人数)。这时候不能只依赖 AVG 自身行为。
- 典型陷阱:
SELECT dept, AVG(salary) FROM emp GROUP BY dept—— 如果某个dept在emp中无记录,它根本不会出现在结果里,不是NULL,而是「压根没这行」 - 要强制让所有部门都出现(包括无人部门),得用
departments表做主表 +LEFT JOIN emp,再对emp.salary聚合:此时AVG(emp.salary)对无人部门返回NULL,可用COALESCE(AVG(emp.salary), 0)统一填0 - 但注意:此时
AVG(emp.salary)的分母是COUNT(emp.salary)(即该部门非空薪资数),不是部门总人数——如果语义要求「部门平均薪资 = 总薪/部门人数」,就必须用SUM(emp.salary) / NULLIF(COUNT(departments.id), 0)并提前关联好
AVG,而是想清楚——你要平均的是「存在的值」,还是「理论上该有的全部值」;后者往往需要显式控制分母,而不是依赖函数默认行为。











