SQL标准无MEDIAN函数,主流数据库均不支持;PostgreSQL 14+可用PERCENTILE_CONT(0.5) WITHIN GROUP,MySQL/SQL Server需用ROW_NUMBER()+COUNT()手动模拟。

SQL标准里根本没有MEDIAN函数
绝大多数主流数据库(MySQL、PostgreSQL 13之前、SQL Server、SQLite)原生不支持MEDIAN()聚合函数。你直接写SELECT MEDIAN(salary) FROM employees GROUP BY dept会报错,比如 PostgreSQL 报function median(numeric) does not exist,MySQL 直接提示Unknown function 'MEDIAN'。这不是语法写错了,是功能压根没实现。
PostgreSQL 14+ 可以用PERCENTILE_CONT(0.5)
从 14 版本起,PostgreSQL 正式支持连续分布百分位函数,中位数就是第 50 百分位:PERCENTILE_CONT(0.5)。它必须配合OVER()或GROUP BY使用,且对排序敏感:
SELECT dept, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median_salary FROM employees GROUP BY dept;
注意点:
-
WITHIN GROUP (ORDER BY ...)不可省略,否则语法错误 - 输入值为
NULL时会被自动忽略(和AVG行为一致) - 若组内行数为偶数,它会线性插值得到中间两个数的平均值(即真正的连续中位数)
- 性能上比手写窗口函数略好,但大数据量仍需索引支持
salary
MySQL 和 SQL Server 得靠窗口函数模拟
MySQL 8.0+ 和 SQL Server 2017+ 支持ROW_NUMBER()和COUNT() OVER(),可以手动定位中间位置:
SELECT dept, AVG(salary) AS median_salary
FROM (
SELECT dept, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary) AS rn,
COUNT(*) OVER (PARTITION BY dept) AS cnt
FROM employees
) t
WHERE rn IN (FLOOR((cnt + 1) / 2), CEIL((cnt + 1) / 2))
GROUP BY dept;
关键细节:
- 用
FLOOR和CEIL覆盖奇偶两种情况:奇数取中间1个,偶数取中间2个再AVG -
ROW_NUMBER()必须带PARTITION BY dept ORDER BY salary,顺序错结果全错 - 如果
salary有重复值,ROW_NUMBER()仍会强制编号(不会并列),所以结果是确定的,但未必符合业务上“值相等即同位”的直觉 - SQL Server 中
CEILING要写成CEILING,MySQL 用CEIL,大小写敏感别拼错
SQLite 和旧版 PostgreSQL 只能建视图或用应用层计算
SQLite 连窗口函数都没有(直到 3.25 才支持部分,且无ROW_NUMBER),旧版 PostgreSQL(PERCENTILE_CONT。这时候硬要在 SQL 层算中位数,要么:
- 在应用代码里查出全部数据,用 Python/JS 排序后取
len//2位置——适合小数据集 - 用子查询嵌套
(SELECT COUNT(*) FROM t t2 WHERE t2.val = (SELECT COUNT(*)/2 FROM t)找中位数行——N²复杂度,万级数据就卡死 - 提前建好物化中位数表,定时更新——适合报表场景,但实时性差
真正上线前得先看执行计划,EXPLAIN一下那个子查询是不是走了索引;没索引的ORDER BY在百万行上跑中位数,等于主动申请加班。










