sql标准无median函数,主流数据库原生不支持;mysql 8.0+用percentile_cont(0.5) within group (order by salary),postgresql支持percentile_cont(插值)和percentile_disc(离散),均需排序与索引优化。

SQL标准里根本没有MEDIAN函数
绝大多数主流数据库(MySQL 8.0之前、PostgreSQL 13之前、SQL Server、SQLite)原生不支持MEDIAN聚合函数。你执行SELECT MEDIAN(salary) FROM employees,大概率会直接报错:ERROR: function median(numeric) does not exist(PostgreSQL)或Unknown function 'MEDIAN'(MySQL)。别被某些BI工具或文档误导——它们可能封装了自定义逻辑,但底层SQL没这玩意儿。
MySQL 8.0+用PERCENTILE_CONT最稳妥
MySQL 8.0.13起支持窗口函数PERCENTILE_CONT,这是获取中位数的官方推荐方式,能正确处理偶数行(取中间两值平均)和奇数行(取正中值),且对海量数据有优化:
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median_salary FROM employees;
- 必须配合
WITHIN GROUP (ORDER BY ...),不能写成窗口形式(如OVER (ORDER BY salary)) - 如果
salary含NULL,默认被忽略;需显式过滤可加WHERE salary IS NOT NULL - 性能依赖
salary字段是否有索引——没索引时,海量数据排序开销极大
PostgreSQL用PERCENTILE_CONT或percentile_disc看需求
PostgreSQL同时提供两个函数:PERCENTILE_CONT返回插值结果(连续型,偶数时自动平均),percentile_disc返回实际存在的离散值(偶数时取下中位数):
-- 连续中位数(推荐,符合统计惯例) SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) FROM employees; <p>-- 离散中位数(返回真实记录中的某个salary值) SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY salary) FROM employees;</p>
- 两者都要求
ORDER BY子句,且仅支持单列排序 - 若表超大,建议在
salary上建索引:CREATE INDEX idx_emp_salary ON employees(salary); - 注意:早期版本(ARRAY +
unnest模拟,性能差且易OOM
SQL Server和旧版MySQL只能手写逻辑
SQL Server 2022+才支持PERCENTILE_CONT,此前及旧版MySQL必须用子查询或CTE模拟排序后取中间位置:
-- SQL Server(兼容2005+)
SELECT AVG(1.0 * salary) AS median_salary
FROM (
SELECT salary, ROW_NUMBER() OVER (ORDER BY salary) AS rn,
COUNT(*) OVER () AS cnt
FROM employees WHERE salary IS NOT NULL
) t
WHERE rn IN ((cnt + 1) / 2, (cnt + 2) / 2);
-
(cnt + 1) / 2和(cnt + 2) / 2用整数除法自动适配奇偶:奇数时两值相同,偶数时取中间两个 - 必须用
AVG(1.0 * salary)避免整数截断(尤其SQL Server) - 海量数据时
ROW_NUMBER()全表排序仍是瓶颈,没索引几乎不可行
中位数计算本身不难,难的是在不牺牲准确性的前提下扛住千万级数据——索引、NULL处理、类型转换,漏掉任何一环,结果就偏了。











