不能在 WHERE 中直接使用 AVG() 或窗口函数,因 SQL 执行顺序为 FROM → WHERE → GROUP BY → HAVING → SELECT,而 AVG() OVER 在 SELECT 阶段才计算,WHERE 阶段不可见,会报“window functions are not allowed in WHERE”等错误。

不能直接用 AVG() 或 STDDEV() 在 WHERE 里判断离群值——SQL 执行顺序决定了它们还没算出来,数据库会报 window functions are not allowed in WHERE 或语法错误。
为什么不能在 WHERE 中直接写 AVG() OVER?
SQL 按照 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY 顺序执行。AVG() OVER 属于 SELECT 阶段才计算,WHERE 阶段根本看不到它。你写 WHERE amount > AVG(amount) OVER (PARTITION BY user_id),数据库直接拒绝执行。
- 常见报错:
window functions are not allowed in WHERE(PostgreSQL/BigQuery)或Invalid use of window function(MySQL 8.0+) - 想“边算边筛”,必须把窗口结果先“带下来”——用 CTE 或子查询保存中间列
-
GROUP BY后也不能直接引用未聚合的原始字段,比如HAVING amount > AVG(amount)是非法的
用 CTE + 窗口函数组合识别分组离群值
这是目前最可靠、可读性最强的通用方案,适用于 PostgreSQL、SQL Server、Oracle、BigQuery 和 MySQL 8.0+。
- 第一步:在 CTE 中用窗口函数算出每组的
AVG(amount)、STDDEV(amount)、COUNT(*) - 第二步:加防护条件,例如
COUNT(*) >= 3(避免单行 std 为 NULL)、NULLIF(STDDEV(amount), 0)(防除零) - 第三步:主查询中用
WHERE amount > avg_amt + 3 * COALESCE(std_amt, 0)过滤
WITH group_stats AS (
SELECT user_id,
AVG(amount) AS avg_amt,
STDDEV(amount) AS std_amt,
COUNT(*) AS cnt
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 3
)
SELECT o.*
FROM orders o
JOIN group_stats g ON o.user_id = g.user_id
WHERE o.amount > g.avg_amt + 3 * COALESCE(g.std_amt, 0);用 PERCENTILE_CONT() 替代均值+标准差更抗噪
当数据有大量 0 值或长尾分布(如少数订单金额极大),STDDEV() 会被拉高,导致阈值过宽、漏判真实异常。此时四分位距(IQR)法更稳。
- 必须写全:
PERCENTILE_CONT(0.25) OVER (PARTITION BY user_id ORDER BY amount)—— 漏掉ORDER BY会返回任意值,不报错但结果不可信 - IQR 上界 =
q3 + 1.5 * (q3 - q1),下界同理;别用固定百分位如PERCENTILE_CONT(0.05),鲁棒性差 - MySQL 8.0+ 不支持
PERCENTILE_CONT(),得用ROW_NUMBER()+COUNT(*)手动逼近,精度可控但写法冗长
在聚合时排除离群值,别用 WHERE 先删行
想算“剔除异常后的部门平均薪资”,不能写 WHERE salary < 100000 GROUP BY dept——这会整行删掉高薪员工,导致某些部门统计人数变少甚至消失。要的是“组内过滤后聚合”。
- 通用写法:
AVG(CASE WHEN salary BETWEEN 5000 AND 80000 THEN salary END),没匹配的自动为 NULL,AVG()自动跳过 - PostgreSQL 可用更简洁的
AVG(salary) FILTER (WHERE salary BETWEEN 5000 AND 80000),但 MySQL/SQL Server 不支持 - 注意:
CASE WHEN ... THEN salary END必须带END,漏写会报syntax error at or near "THEN"
真正容易被忽略的点是:离群值没有绝对定义。全局均值对分组无效,固定阈值对业务失真,而 PERCENTILE_CONT() 若没配对 PARTITION BY 和 ORDER BY,结果就完全不可信——它不报错,但返回的是垃圾。

















