跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

怎样在SQL中实现分组后的中位数计算_使用MEDIAN函数(针对支持的DB)

MySQL 8.0.33+ 支持 MEDIAN() 窗口函数,但仅限于 OVER(PARTITION BY ... ORDER BY ...) 形式,不可用于 GROUP BY;它返回每组内按序排列的累积中位数,非传统分组独立中位数,且不支持 DISTINCT 或嵌套聚合。 MySQL 8.0+ 怎么用
MEDIAN()
计算分组中位数 MySQL 8.0.33 起才原生支持
MEDIAN()
窗口函数,但仅限于窗口模式(不能直接在
GROUP BY
中用),且必须配合
OVER()
。常见错误是写成
SELECT MEDIAN(score) FROM t GROUP BY class
—— 这会报错
ERROR 1111 (HY000): Invalid use of group function
。 正确做法是先用窗口函数为每组排序编号,再取中间位置的值;或者改用
MEDIAN() OVER (PARTITION BY class ORDER BY score)
,但注意:它返回的是「累积中位数」(cumulative median),不是每组独立中位数。
MEDIAN()
在 MySQL 中不接受
DISTINCT
,也不能嵌套在聚合上下文中 若数据量大,
MEDIAN() OVER (...)
可能比手动模拟慢,因需全组排序 空值(
NULL
)会被自动过滤,不影响中位数计算逻辑 PostgreSQL 怎么安全地用
PERCENTILE_CONT(0.5)
替代
MEDIAN()
PostgreSQL 没有
MEDIAN()
函数,但
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY x)
是标准、可靠、支持分组的替代方案。它在
GROUP BY
中可直接使用,语义清晰,且对偶数个元素自动插值(如 [1,3] → 2.0)。 容易踩的坑是漏掉
WITHIN GROUP
子句,或把
ORDER BY
写成列别名(如
ORDER BY avg_score
),而实际只能用原始列或表达式。 必须写成
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY score)
,括号和子句缺一不可 若分组后某组全为
NULL
,结果为
NULL
,不会报错 性能上,它依赖索引加速排序;没索引时,大数据量分组可能显著变慢 SQLite 和旧版 MySQL 怎么手动算分组中位数(无内置函数) SQLite 完全没有中位数函数,旧版 MySQL(MEDIAN(),这时得靠变量或自连接模拟「第 N/2 小的值」。核心思路是:对每组按值排序,给每行打序号,再筛选序号落在中位区间的行。 例如 MySQL 5.7 的典型写法:
SELECT class, AVG(score) AS median FROM ( SELECT class, score, @row := IF(@prev = class, @row + 1, 1) AS row_num, @prev := class FROM t, (SELECT @row := 0, @prev := '') r ORDER BY class, score ) ranked JOIN ( SELECT class, COUNT(*) AS cnt FROM t GROUP BY class ) cnts USING (class) WHERE row_num IN (FLOOR((cnt + 1)/2), CEIL((cnt + 1)/2)) GROUP BY class;
这个写法问题不少:依赖用户变量执行顺序(MySQL 8.0+ 已不推荐)、无法并行、空值处理模糊、且
FLOOR/CEIL
对奇偶逻辑要小心匹配。 SQLite 推荐用两次
ROW_NUMBER()
窗口(需 3.25+):一次正序、一次倒序,找两个序号都 ≥ 总数一半的行 所有手动实现都默认忽略
NULL
;若需保留,得先用
COALESCE
或子查询预处理 当某组只有一行时,确保
FLOOR
和
CEIL
结果一致,否则会漏数据 为什么不能直接用
AVG()
或
PERCENTILE_DISC
代替中位数 中位数的核心价值是抗异常值,而
AVG()
不是替代品——哪怕只是想“快速近似”,在偏态分布下误差可能极大(比如 99 个 1 和 1 个 1000,
AVG
≈ 10.99,
MEDIAN
= 1)。
PERCENTILE_DISC(0.5)
在 PostgreSQL 中存在,但它返回「离散值」(即组内真实存在的某个值),不插值。例如 [1,2,4,5] 返回 2 而非 3.0。这和多数人理解的中位数定义(连续型)不一致,尤其在统计报表中易引发歧义。
PERCENTILE_DISC
在偶数长度时不保证返回哪个中间值(PostgreSQL 文档注明“implementation-defined”) 某些 BI 工具(如 Metabase)的「中位数」字段其实是调用
PERCENTILE_CONT
,但导出 SQL 时可能悄悄替换成
AVG
,务必检查生成语句 跨数据库移植时,
MEDIAN()
行为差异最大:Oracle 返回近似值,MySQL 8.0.33+ 返回精确值,而 SQL Server 根本没这个函数 真正麻烦的不是语法怎么写,而是确认你用的数据库版本是否真的支持、以及那个“支持”是不是你理解的支持——比如 MySQL 的
MEDIAN()
看似来了,实则不能直接进
GROUP BY
,还得绕一圈。动手前,先
SELECT VERSION()
和查文档确认行为细节,比抄示例代码重要得多。

相关文章