MySQL 8.0.13+ 支持 PERCENTILE_CONT(0.5) 和 PERCENTILE_DISC(0.5) 计算中位数,前者线性插值(如[4,6]→5.0),后者取实际值(如[4,6]→4);必须用 WITHIN GROUP (ORDER BY ...) 聚合语法,不支持 OVER 窗口形式,且空值自动忽略。

MySQL 8.0+ 直接用 PERCENTILE_CONT 和 PERCENTILE_DISC
MySQL 8.0.13 起原生支持窗口函数计算中位数和百分位数,不用手写排序逻辑。关键区别在于:PERCENTILE_CONT 插值计算(适合连续分布),PERCENTILE_DISC 取实际存在的值(适合离散数据)。
常见错误是忽略 OVER() 中的 ORDER BY —— 没有它会报错或返回 NULL:
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median FROM employees;
注意:WITHIN GROUP 是强制语法,不能换成 OVER (ORDER BY ...);PERCENTILE_CONT 不接受 OVER 窗口定义,只接受聚合式写法。
-
PERCENTILE_CONT(0.5)等价于中位数,但0.25、0.9也能直接算任意百分位 - 若数据为空或全为 NULL,结果为 NULL,需提前
WHERE salary IS NOT NULL - 性能上,内部会触发临时排序,大数据量时注意索引覆盖(比如在
salary上建索引)
PostgreSQL 用 percentile_cont 和 percentile_disc
PostgreSQL 的函数名小写,且必须配合 ORDER BY 和窗口定义,不支持 MySQL 那种聚合式写法:
SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY score) FROM test_scores;
如果要按分组分别算中位数(比如每个部门的薪资中位数),得用窗口函数加子查询或 CTE:
SELECT dept,
percentile_cont(0.5) WITHIN GROUP (ORDER BY salary)
OVER (PARTITION BY dept) AS median_salary
FROM employees;
容易踩的坑:
- 误写成
percentile_cont(0.5) OVER (ORDER BY salary)—— 这是语法错误,WITHIN GROUP不可省略 -
percentile_cont返回double precision,而原始字段可能是integer,做比较时注意类型隐式转换 - 空值默认被排除,但若想显式控制,得先用
FILTER (WHERE ...)或子查询清洗
SQLite 和旧版 MySQL 只能手写排序 + 行号模拟
没有内置函数时,核心思路是:排序后取中间位置的行。难点在奇偶判断和行号生成 —— SQLite 用 ROW_NUMBER()(3.25+),老 MySQL 只能靠变量模拟。
SQLite 示例(中位数):
WITH ranked AS (
SELECT value,
ROW_NUMBER() OVER (ORDER BY value) AS rn,
COUNT(*) OVER () AS cnt
FROM data
)
SELECT AVG(value) AS median
FROM ranked
WHERE rn IN ((cnt + 1) / 2, (cnt + 2) / 2);
说明:
-
(cnt + 1) / 2和(cnt + 2) / 2自动兼容奇偶:奇数时两值相等,偶数时取中间两个 - 必须用
AVG()包一层,否则偶数长度下会返回两行 - SQLite 的
ROW_NUMBER()从 1 开始,别用0基起点混淆
MySQL 5.7 及更早版本需用用户变量,但存在执行顺序不可靠风险,生产环境慎用。
百分位数近似计算:用 APPROX_QUANTILES(BigQuery)或采样
超大数据集(如亿级日志)跑精确百分位极慢,BigQuery 提供 APPROX_QUANTILES,返回数组:
SELECT APPROX_QUANTILES(response_time, 100)[OFFSET(50)] AS p50,
APPROX_QUANTILES(response_time, 100)[OFFSET(90)] AS p90
FROM logs;
要点:
-
APPROX_QUANTILES(x, N)把数据分成 N 段,[OFFSET(k)]取第 k 个(0-based),所以 p50 是[OFFSET(50)],不是[OFFSET(0.5)] - 误差率通常
- ClickHouse 用
quantile(0.5)(x),语法更直白,但同样属近似算法(除非加quantileExact)
真正麻烦的不是怎么写,而是搞清业务要的是“最接近的已有值”还是“理论分割点”,选错函数会导致报表偏差。比如监控告警用 p99,用 DISC 可能漏掉尖峰,用 CONT 又可能虚高——得看数据分布和容忍度。

















