MEDIAN不是标准SQL函数,主流数据库普遍不支持;PostgreSQL 14+用percentile_cont(0.5) WITHIN GROUP (ORDER BY col)计算分组中位数,MySQL 8.0+需用ROW_NUMBER()和COUNT()模拟,Oracle虽有MEDIAN()但不支持GROUP BY。

MEDIAN 函数在标准 SQL 中并不存在 —— 大多数主流数据库(如 MySQL、PostgreSQL 13 之前、SQL Server)根本不支持 MEDIAN 作为内置聚合函数。
只有少数系统原生支持:
- PostgreSQL 14+ 提供了
percentile_cont(0.5)作为中位数等价实现,但不是叫MEDIAN - Oracle 支持
MEDIAN(),但仅限单列、非分组场景,且不支持GROUP BY - BigQuery 和 Redshift 提供
APPROX_QUANTILES或PERCENTILE_CONT,但同样没有MEDIAN
所以,别在 WHERE 或 SELECT 里直接写 MEDIAN(col) —— 99% 情况下会报错:ERROR: function median(double precision) does not exist(PostgreSQL)、Unknown function 'MEDIAN'(MySQL/Spark SQL)
PostgreSQL 14+ 怎么算精确中位数?
用percentile_cont(0.5),它返回连续分布下的第 50 百分位值(即中位数),支持 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 ...)是必须的,不能省略括号和ORDER BY - 参数必须是
0.5,写50或'50%'都错 - 如果数据为空组,结果是
NULL,不会报错,但需业务侧判断是否可接受
MySQL 8.0+ 没有 MEDIAN,怎么手动算?
得靠行号模拟排序后取中间位置。常见写法是用ROW_NUMBER() 和总行数对比:
SELECT AVG(salary) AS median_salary
FROM (
SELECT salary,
ROW_NUMBER() OVER (ORDER BY salary) AS rn,
COUNT(*) OVER () AS cnt
FROM employees
) t
WHERE rn IN (FLOOR((cnt + 1) / 2), CEIL((cnt + 1) / 2));
关键细节:
- 必须用
AVG()包一层,因为偶数行时要取中间两值的平均 -
FLOOR和CEIL确保奇偶统一处理,比如 cnt=4 → rn IN (2,3);cnt=5 → rn IN (3,3) - 如果表很大,
OVER()窗口计算开销明显,比percentile_cont慢不少
SQLite 和旧版 PostgreSQL 怎么办?
这些系统连ROW_NUMBER 都不支持(SQLite 3.25+ 才有),只能靠子查询+计数硬算,性能差且易出错:
SELECT AVG(salary) FROM (
SELECT t1.salary
FROM employees AS t1
WHERE (
SELECT COUNT(*) FROM employees WHERE salary < t1.salary
) IN (
(SELECT (COUNT(*) - 1) / 2 FROM employees),
(SELECT COUNT(*) / 2 FROM employees)
)
);
问题很实际:
- 每行都触发两次相关子查询,N² 复杂度,万级数据就卡住
- 对重复值敏感:如果大量 salary 相同,
COUNT(*) WHERE salary < t1.salary可能跳过中位区间 - NULL 值默认被忽略,但没显式
WHERE salary IS NOT NULL就可能混入意外结果
中位数看着简单,但跨数据库实现差异极大;最易被忽略的是:排序依据字段存在 NULL 或大量重复值时,不同方法返回结果可能不一致 —— 别只测正例,一定要用含 NULL、全相同值、奇偶边界数据验证。

















