月初对账,一句查询写出来少了三十多万,查了两小时才发现是where里筛掉了状态为空的记录,而count(字段)本身又不统计null。Oracle的聚合函数就那么几个,坑全在null和分组上。
五个基础函数与null的关系
count星号数行数,count(字段)只数该字段非null的行,两者结果可能差很多。sum和avg会自动跳过null,所以avg的分母是非null的个数,不是总行数。想让null参与计算,用nvl给字段套一个默认值。max和min对字符类型也有效,按排序规则比大小。
group by 的规则要记死
select里出现的非聚合列,必须全部写进group by,否则报ORA-00979。反过来,group by里有、select里没有的列是允许的。想按表达式分组,比如按月份,group by里要写同样的表达式,不能只写select里定义的别名。
select deptno, count(*) cnt, sum(sal) total from emp where sal > 0 group by deptno having sum(sal) > 10000;having 和 where 的分工
where在分组前过滤行,having在分组后过滤组。聚合函数不能写在where里,写成where sum(sal) > 1000会直接报错,得挪到having。having里可以用select里定义的别名,这是Oracle相对宽松的地方,但为了可读性别依赖它。
进阶:rollup 与 listagg
group by rollup(deptno, job)会在结果里追加小计行和总计行,小计行里被卷起的列为null。用grouping函数能区分这是真null还是小计行。要把一组值拼成字符串,用listagg加within group指定排序字段,这是11g以后才有的函数,拼接结果默认有4000字节上限。
- 对账先查null:count星号和count(字段)各跑一遍,差值就是null行数。
- 分母要确认:avg跳过null,算人均时容易偏高。
- 别在where用聚合:换having,或者先子查询过滤再聚合。