SQL聚合函数是数据分析与后台开发岗位笔试中的高频考点,其中SUM、AVG、COUNT三者既能单独使用,也常和GROUP BY、HAVING组合出现。理解它们的底层统计逻辑与边界情况,是通过面试的关键。

一、COUNT的两种写法与NULL陷阱
COUNT在面试题中最容易挖坑的地方是“统计什么”。COUNT(*)统计所有行,无论字段是否为NULL;COUNT(列名)只统计该列非NULL的行。很多候选人误以为二者等价,在要求“统计有邮箱的员工数”时写了COUNT(*),导致结果偏大。
以下示例用员工表演示差异。表中第三行邮箱为NULL,业务上不应计入“有邮箱人数”。
-- 员工表 emp(id, name, email)
-- 数据:
-- 1, 张三, z@ipipp.com
-- 2, 李四, l@ipipp.com
-- 3, 王五, NULL
SELECT COUNT(*) AS total_row,
COUNT(email) AS has_email
FROM emp;
-- 返回: total_row=3, has_email=2
另一个冷门点是COUNT(DISTINCT 列)可以去重计数。当面试问“每个部门有多少不同工种”时,必须用DISTINCT,否则会把同工种重复计算。它比先GROUP BY再COUNT(*)更简洁,但在超大数据集上 DISTINCT 排序开销更高,这是面试中谈优化时常说的权衡。
二、SUM与AVG的运算本质
AVG(col)在关系数据库里严格等于SUM(col)除以COUNT(col)(均忽略NULL),而不是除以总行数。这个定义决定了:如果一列有一半是NULL,AVG只会用另一半求和再除以非NULL行数,结果可能比直觉高。
看一个工资补贴场景,bonus列部分为空,直接算平均补贴容易误读。
-- emp(id, name, salary, bonus)
-- 1, 张三, 8000, 500
-- 2, 李四, 9000, NULL
-- 3, 王五, 7000, 300
SELECT SUM(bonus) AS sum_b,
AVG(bonus) AS avg_b,
SUM(bonus)/COUNT(bonus) AS calc_avg
FROM emp;
-- sum_b=800, avg_b=400, calc_avg=400
SUM对NULL的处理是“忽略”,不是当0。若用COALESCE(bonus,0)包一层再SUM,结果会变成800+0+300=1100,语义从“已发补贴总额”变成“应发补贴总额”,面试里必须根据需求选。AVG返回浮点,在金额报表中常配合ROUND(AVG(bonus),2)控制精度,避免浮点误差被展示出来。
三、GROUP BY与HAVING中的实战组合
聚合函数不能写在WHERE后面,这是语法错;过滤分组要用HAVING。下面按部门算平均薪资,并只保留平均大于7500的部门。
-- dept_emp(dept_id, name, salary)
SELECT dept_id,
SUM(salary) AS dept_sum,
AVG(salary) AS dept_avg,
COUNT(*) AS emp_cnt
FROM dept_emp
GROUP BY dept_id
HAVING AVG(salary) > 7500;
执行顺序上,数据库先WHERE筛行,再GROUP BY分组,然后算聚合,最后HAVING滤组。面试常问“WHERE和HAVING区别”,核心就是“行级过滤”和“组级过滤”。若在HAVING里用COUNT(*) > 3,意思是组内人数超3才输出,这和先算再筛的逻辑完全一致。
联表时聚合更易出错。比如左联部门表后COUNT(emp.id)与COUNT(dept.id)不同:前者在没员工时得0,后者因部门本身存在而得1。写面试题时要在SELECT里明确列名,防止考官追问“你数的是哪边的行”。
四、常见面试题作答模板
当考官给出场景“统计各城市订单数、总金额、客单价”,标准答法是用GROUP BY city配合COUNT(order_id)、SUM(amount)、SUM(amount)/COUNT(order_id)。若补充“排除金额为空订单”,则在WHERE写amount IS NOT NULL,而不是在HAVING里再判。
最后提醒,聚合函数嵌套如AVG(SUM(x))非法,必须先GROUP BY出中间表再外层聚合;而窗口函数SUM() OVER()虽不分组却也属聚合类,面试进阶题常拿来对比GROUP BY的“折叠行”与“保留行”差异。把上述NULL、执行序、联表计数三点讲清,SUM、AVG、COUNT实战题基本能拿满分。