主流SQL引擎不支持MEDIAN()窗口函数,需按数据库选方案:PostgreSQL/Oracle/SQL Server用PERCENTILE_CONT(0.5) WITHIN GROUP(聚合函数,需GROUP BY);MySQL 8.0+/SQLite须手写ROW_NUMBER()+COUNT()逻辑;SQL Server手写时注意AVG类型转换和CEILING拼写;避免PERCENT_RANK()/NTILE()等不可靠替代。

没有通用的 MEDIAN() 窗口函数,也不能直接写 MEDIAN(x) OVER (PARTITION BY group_col)——所有主流 SQL 引擎都不支持这种语法。真要按组算中位数,得看数据库类型选路径:PostgreSQL/Oracle/SQL Server 可用 PERCENTILE_CONT(0.5) 聚合;MySQL 8.0+ 和 SQLite 必须手写 ROW_NUMBER() + COUNT() 逻辑。
PostgreSQL / Oracle / SQL Server:用 PERCENTILE_CONT(0.5) WITHIN GROUP 最稳
这是标准 SQL 中语义最清晰、结果最可靠的中位数计算方式,但它不是窗口函数,而是**聚合函数**,必须搭配 GROUP BY 使用。
- 写法必须是:
SELECT group_col, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY value_col) FROM t GROUP BY group_col - 别漏掉
WITHIN GROUP (ORDER BY ...),少了这个会报错或返回空 -
value_col不能是NULL(函数自动跳过NULL,但若整组全NULL,结果为NULL) - 如果想让每行都显示同组中位数(比如加一列
group_median),不能直接套OVER,得用子查询或LATERAL关联
MySQL 8.0+:手写 ROW_NUMBER() + COUNT() 是唯一可行路径
MySQL 不支持 PERCENTILE_CONT,也没有 MEDIAN(),只能靠编号 + 总数判断中间位置。
- 核心子查询结构必须包含两部分:
ROW_NUMBER() OVER (PARTITION BY group_col ORDER BY value_col)和COUNT(*) OVER (PARTITION BY group_col) - 中位位置统一用
FLOOR((cnt + 1) / 2)和CEIL((cnt + 1) / 2)表达,兼容奇偶 - 注意:MySQL 的
AVG()对INT列会自动转DECIMAL,没问题;但若后续要导出或对接其他系统,建议显式CAST(val AS DECIMAL(10,2)) - 常见错误是漏写
PARTITION BY,导致全表排序编号,分组失效
SQL Server:同样要手写,但数值类型容易踩坑
SQL Server 支持 PERCENTILE_CONT,但仅限于 2012+ 版本且需配合 GROUP BY;若用窗口形式(OVER),它返回的是每行对应的插值结果,不是每组一个值。
- 手写方案和 MySQL 几乎一样,但
AVG(INT)默认仍返回INT,小数会被截断——必须写成AVG(CAST(val AS FLOAT))或AVG(1.0 * val) -
CEILING()函数名拼写是CEILING,不是CEIL(后者在 SQL Server 中不识别) - 如果数据量大,
ORDER BY value_col没索引支撑时,排序会很慢;建议在(group_col, value_col)上建联合索引
别被 PERCENT_RANK() 或 NTILE(2) 坑了
这两个函数看起来像能定位中位,实际在重复值、偶数行等场景下结果不稳定,根本不能替代中位数逻辑。
-
PERCENT_RANK()对[1,2,2,3]返回[0, 0.33, 0.33, 1],没有精确的0.5,强行取最接近的行会偏移 -
NTILE(2) OVER (ORDER BY x)在 4 行时分桶为[1,1,2,2],取tile = 2的最小值 ≠ 中位数(应是第 2 和第 3 行平均) - 两者都无法直接用于聚合,仍需嵌套过滤,代码更绕、可读性差、结果不可靠
真正麻烦的不是写法本身,而是 NULL 处理、重复值排序行为、数值类型隐式转换这些细节——它们不会报错,但会让中位数悄悄偏移。动手前先确认:你到底需要每组一个汇总值,还是每行都带中位数?后者多数时候只是冗余计算。

















