MySQL 8.0.13+ 支持 PERCENTILE_CONT(0.5) 窗口函数,必须配合 OVER(PARTITION BY...ORDER BY...) 使用,不能用于 GROUP BY 聚合;PostgreSQL 则支持直接聚合写法:PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY col) 配合 GROUP BY。

MySQL 8.0+ 直接用 PERCENTILE_CONT 最省事
MySQL 8.0.13 起原生支持中位数计算,不用写复杂子查询或变量。关键是它属于窗口函数,必须配合 OVER() 使用,且只接受常量 0.5 作为参数:
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median_salary FROM employees;
注意两点:一是 WITHIN GROUP (ORDER BY ...) 不可省略,二是该函数不能直接用于 GROUP BY 分组聚合(比如按部门算中位数),得套一层窗口或子查询。如果真要分组,得这么写:
SELECT dept,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary)
OVER (PARTITION BY dept ORDER BY salary ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS median_by_dept
FROM employees;
但这样会重复输出每行,实际更常用的是先 GROUP BY + 子查询或 CTE 配合 ROW_NUMBER()。
PostgreSQL 用 PERCENTILE_CONT(0.5) 更简洁
PostgreSQL 的 PERCENTILE_CONT 可直接用于聚合上下文,天然支持 GROUP BY:
SELECT dept, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median_salary FROM employees GROUP BY dept;
无需窗口、无需子查询,语义清晰。但它要求输入列不能为 NULL(NULL 值会被自动过滤),如果字段本身含 NULL 且你想保留逻辑,得提前用 WHERE salary IS NOT NULL 过滤,否则结果可能比预期少。
另外,PERCENTILE_CONT 返回的是插值结果(两个中间值的线性插值),如果是整数列但需要整数中位数,得套 FLOOR 或 ROUND —— 但要注意这已不是严格定义的中位数了。
SQLite 和老版本 MySQL 只能手写逻辑,重点防越界和偶数长度
SQLite 没有内置中位数函数,MySQL 5.7 及更早也不支持。通用解法是用 ROW_NUMBER() 定位中间位置,再用条件聚合取值。典型错误是没处理偶数行时“两个中间值”的平均问题,或 OFFSET/LIMIT 在空表/单行时崩掉。
安全写法要点:
- 用
(COUNT(*) + 1) / 2和(COUNT(*) + 2) / 2算出两个目标序号(兼容奇偶) - 用
ROW_NUMBER() OVER (ORDER BY x)排序编号,别用RANK()(重复值会导致序号跳变) - 用
AVG()包裹最终取值,自动处理奇偶:奇数时两序号相同,AVG不影响;偶数时取均值 - 外层加
WHERE rn IN (...)后必须GROUP BY或聚合,否则报错
示例(MySQL 5.7 兼容):
SELECT AVG(val) AS median FROM ( SELECT val, ROW_NUMBER() OVER (ORDER BY val) AS rn, COUNT(*) OVER() AS cnt FROM numbers ) t WHERE rn IN (FLOOR((cnt + 1) / 2), CEIL((cnt + 1) / 2));
SQL Server 的 PERCENTILE_CONT 必须配 OVER(),不能裸用
SQL Server 支持 PERCENTILE_CONT,但和 MySQL 类似,它只能作为窗口函数,不能像 PostgreSQL 那样直接在 SELECT 中聚合。常见误写是漏掉 OVER 或写成 OVER()(空括号会报错)。
正确姿势:
- 不分组:用
OVER (ORDER BY col),但结果每行都一样,通常再包一层SELECT TOP 1 - 分组:必须用
PARTITION BY,例如OVER (PARTITION BY dept ORDER BY salary) - 排序字段必须明确,且
ORDER BY内不能有表达式(如ORDER BY ABS(x)在某些版本不被允许)
分组中位数典型写法:
SELECT DISTINCT dept,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary)
OVER (PARTITION BY dept) AS median_salary
FROM employees;
中位数计算最易被忽略的其实是数据分布前提:它对异常值不敏感,但对 NULL 和重复值非常敏感。无论用哪种函数,先确认业务是否允许忽略 NULL、是否需去重、是否接受插值结果——这些决定远比选哪个函数更重要。

















