核心思路是先用子查询计算全表平均值,再在外层WHERE中比较;因聚合函数不能直接用于WHERE,须避免ERROR 1111,正确写法为SELECT dept, sales FROM departments WHERE sales > (SELECT AVG(sales) FROM departments)。

直接用子查询计算平均值再比较
核心思路是先算出所有部门的平均销售额,再把每个部门的销售额和这个平均值比。不能在 WHERE 里直接写 AVG(sales),因为聚合函数不能和普通字段混用——会报错 ERROR 1111 (HY000): Invalid use of group function。
正确写法是把平均值算在子查询里:
SELECT dept, sales FROM departments WHERE sales > (SELECT AVG(sales) FROM departments);
注意:这里假设 departments 表每行代表一个部门,sales 是该部门销售额(数值型)。如果数据是明细订单表(比如每行是一笔销售记录),就得先按部门 GROUP BY dept 汇总,再套一层。
用窗口函数避免重复扫描(MySQL 8.0+ / PostgreSQL)
子查询方式会执行两次全表扫描:一次算平均值,一次过滤。数据量大时慢。窗口函数能一次扫完:
SELECT dept, sales FROM ( SELECT dept, sales, AVG(sales) OVER() AS avg_sales FROM departments ) t WHERE sales > avg_sales;
关键点:
-
AVG(sales) OVER()不带PARTITION BY,表示整张表的平均值,每行都一样 - 必须用派生表(别名
t)包装,否则WHERE不能引用窗口函数结果 - SQLite 和旧版 MySQL 不支持窗口函数,得退回子查询方案
处理空值和零值部门
如果某些部门 sales 是 NULL,AVG() 默认忽略它们,但 WHERE sales > ... 会让这些行直接被过滤掉(因为 NULL > X 结果为 UNKNOWN)。这通常符合预期,但得确认业务逻辑是否真要排除空部门。
如果想显式控制空值行为:
- 把
NULL当作 0 参与平均计算:AVG(COALESCE(sales, 0)) - 保留空部门但不参与比较:
WHERE sales IS NOT NULL AND sales > (SELECT AVG(sales) FROM departments WHERE sales IS NOT NULL) - 部门销售额为 0 时,即使低于平均值也会被筛掉——这是数值比较的自然结果,不是 bug
性能敏感时记得加索引
当 departments 表很大(比如百万行以上),光靠主键索引不够快。对筛选字段 sales 建索引能显著加速:
CREATE INDEX idx_dept_sales ON departments(sales);
但要注意:
- 如果表写入频繁,索引会拖慢
INSERT/UPDATE - 复合查询(比如还要按
dept排序)可能需要联合索引,如(sales, dept) - 执行前用
EXPLAIN看是否真的用了索引,避免“建了等于没建”
平均值本身很小,但“高于平均值”这个条件容易让人忽略数据分布——如果大部分部门销售额集中在均值附近,结果集可能意外地大或小;真要分析趋势,还是得看分位数或直方图。

















