能,但仅部分数据库支持;mysql 5.7+、postgresql、sql server 2012+ 支持 avg(distinct salary),sqlite 和旧版 mysql 会报错,需用子查询 select avg(salary) from (select distinct salary from emp) as t 兼容实现。

AVG(DISTINCT salary) 能直接去重后求平均吗?
能,但只在部分数据库里支持。MySQL 5.7+、PostgreSQL、SQL Server 2012+ 都允许 AVG(DISTINCT salary),但 SQLite 和旧版 MySQL(如 5.6)会报错:ERROR 1111 (HY000): Invalid use of group function。
原因不是语法写错了,而是这些引擎不支持在聚合函数里嵌套 DISTINCT 修饰非分组字段——它把 AVG(DISTINCT ...) 当作非法组合处理。
- 想兼容老版本或 SQLite?得绕开,用子查询先去重再算均值
- PostgreSQL 里
AVG(DISTINCT salary)和SELECT AVG(salary) FROM (SELECT DISTINCT salary FROM emp) t结果一致,但前者更简洁 - 注意:
DISTINCT作用于整行去重,但这里只传一个字段,所以实际是去重该字段的值
用子查询实现跨数据库兼容的去重平均
这是最稳妥的做法,所有主流 SQL 引擎都支持。核心思路是:先用 SELECT DISTINCT salary FROM emp 拿出唯一薪资值,再对这个结果集调用 AVG()。
SELECT AVG(salary) FROM (SELECT DISTINCT salary FROM emp) AS unique_salaries;
常见踩坑点:
- 子查询必须起别名(如
AS unique_salaries),否则 MySQL 会报Every derived table must have its own alias - 如果
salary字段含NULL,DISTINCT会保留一个NULL,而AVG()自动忽略NULL,所以最终结果不受影响 - 性能上,如果
emp表极大且salary去重后仍很多,这个子查询可能比直接AVG(DISTINCT ...)稍慢,但差异通常可忽略
GROUP BY 和窗口函数不适合这个需求
有人试过加 GROUP BY salary 再套 AVG(),结果出错或语义错误。因为 GROUP BY salary 是按每个薪资值分组,每组只有一行(去重效果),但 AVG(salary) 在每组里等于自身,最后还得再聚合一次,反而绕远了。
窗口函数比如 AVG(salary) OVER (PARTITION BY salary) 更不合适——它不会减少行数,只是给每行打一个“本薪资组的平均值”,全是重复数字,毫无意义。
-
GROUP BY适合“每个部门的平均薪资”,不是“所有薪资去重后的平均” - 窗口函数解决的是“当前行在某范围内的统计”,不是集合去重汇总
- 硬套只会让逻辑变模糊,还容易引发
ONLY_FULL_GROUP_BY报错
为什么不能用 WHERE 过滤重复?
WHERE 只能筛行,不能去重。比如写 WHERE salary = (SELECT MIN(salary) FROM emp),那只剩最低薪一行,完全不是“取所有不重复薪资再平均”的意思。
也有人想用 ROW_NUMBER() OVER (PARTITION BY salary ORDER BY id) 标记首行,再 WHERE rn = 1 ——这确实能模拟去重,但比子查询更重、更啰嗦,且在没有主键或排序依据时行为不稳定。
- 去重本质是集合操作,
DISTINCT或GROUP BY才是语义匹配的工具 - 用分析函数强行实现,属于用大锤砸核桃,还容易漏掉
NULL处理细节 - 真正要控制去重逻辑(比如“每个薪资只取最早入职者”),才值得引入窗口函数
真正要注意的是:DISTINCT 去重基于值的完全相等,浮点数、空格、大小写都算不同值。如果业务上 “5000.00” 和 “5000” 应视为同一薪资,得先用 ROUND() 或 TRIM() 统一格式,再套去重逻辑——这点常被忽略,查出来平均数偏差却找不到原因。










