mysql 8.0.13+ 支持 percentile_cont(0.5) 聚合写法(within group + group by),非窗口语法;误用 over 会报错,且需显式过滤 null;postgresql 同样支持该聚合形式,sqlite 和旧版 mysql 需手写行号模拟。

MySQL 8.0+ 直接用 PERCENTILE_CONT 计算分组中位数
MySQL 8.0.13 起原生支持窗口函数 PERCENTILE_CONT,这是最接近“标准中位数语义”的方案——它按分组排序后插值计算,对偶数个值返回两个中间数的平均值(如 [1,3,5,7] → 4.0)。
关键点:PERCENTILE_CONT(0.5) 必须搭配 OVER (PARTITION BY ... ORDER BY ...),且 ORDER BY 列不能为 NULL(否则该行被排除出计算)。
示例:
SELECT dept, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median_salary FROM employees GROUP BY dept;
注意:WITHIN GROUP 是聚合语法,不是窗口语法;这里不用 OVER,而是传统聚合写法。若你误写成 OVER (PARTITION BY dept ORDER BY salary),会报错 Window function 'percentile_cont' is not allowed in this context。
PostgreSQL 用 PERCENTILE_CONT + GROUP BY 最稳妥
PostgreSQL 的 PERCENTILE_CONT(0.5) 是聚合函数,必须配合 GROUP BY 使用,行为与 MySQL 类似但语法更统一。
常见坑:
-
PERCENTILE_CONT返回double precision,若原始列为integer,结果带小数(如中位数是 5 → 显示为 5.0) - 空值默认被忽略;若想把
NULL当作最小值参与排序,得先用COALESCE(salary, -999999)处理 - 若分组内所有值都是
NULL,函数返回NULL,不会报错
示例:
SELECT team, ROUND(PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY score), 2) AS median_score FROM results GROUP BY team;
SQLite 和旧版 MySQL 只能手写中位数逻辑
这些引擎不支持 PERCENTILE_CONT,必须靠子查询 + 行号模拟。核心思路是:对每组数据按目标列排序,取行号为 FLOOR((n-1)/2) 和 CEILING((n-1)/2) 的两行(n 为组内非空行数),再求均值。
实操难点:
- SQLite 没有
ROW_NUMBER()窗口函数(3.25+ 才支持),低于此版本只能用自关联计数,性能极差 - MySQL 5.7 需用变量模拟序号,但变量在
GROUP BY中行为不稳定,容易错位 - 必须显式过滤
WHERE value IS NOT NULL,否则COUNT(*)和排序结果不一致
MySQL 5.7 兼容写法(慎用于大表):
SELECT dept,
AVG(salary) AS median_salary
FROM (
SELECT dept, salary,
@row := IF(@prev = dept, @row + 1, 1) AS row_num,
@prev := dept
FROM employees
CROSS JOIN (SELECT @row := 0, @prev := '') AS _
WHERE salary IS NOT NULL
ORDER BY dept, salary
) AS ranked
JOIN (
SELECT dept, COUNT(*) AS cnt FROM employees WHERE salary IS NOT NULL GROUP BY dept
) AS counts USING (dept)
WHERE row_num IN (FLOOR((cnt - 1) / 2) + 1, CEIL((cnt - 1) / 2) + 1)
GROUP BY dept;
中位数和 MEDIAN 函数名本身是陷阱
别被名字误导:MEDIAN 不是 SQL 标准函数,绝大多数数据库根本不认这个单词。PostgreSQL 有 median() 聚合函数,但它属于 intarray 扩展,需手动启用且只支持整数数组输入,不能直接用于表列。
真正跨库可用的只有 PERCENTILE_CONT(0.5)(MySQL 8.0+/PostgreSQL/SQL Server/Oracle),其余方案本质都是绕路。如果业务要求严格兼容 SQLite 或老 MySQL,建议把中位数计算移到应用层——数据库只负责排序和分页,中位数由 Python 的 statistics.median 或 JavaScript 的 median([…]) 算,反而更稳、更可测。











