percentile_cont能算中位数是因为它基于连续分布模型,传入0.5(第50百分位)后通过线性插值返回理论中位数——偶数行时取中间两值平均,奇数行时取中间值;必须配合over()或within group使用,且order by排序不可省略。

PERCENTILE_CONT 为什么能算中位数
PERCENTILE_CONT 是窗口函数,它基于连续分布模型插值计算分位点。中位数本质是第 50 百分位(即 0.5),所以传入 0.5 就能返回理论上的中位数值——哪怕原始数据个数为偶数,它也会在中间两个数之间线性插值得到精确结果。
注意:它必须配合 OVER() 使用,不能像聚合函数那样直接写 SELECT PERCENTILE_CONT(0.5) FROM t,否则会报错 ERROR: PERCENTILE_CONT requires a window specification。
常见错误是漏写 ORDER BY 子句——PERCENTILE_CONT 的 OVER 必须包含 ORDER BY,否则 PostgreSQL 会拒绝执行;SQL Server 虽允许省略,但结果无意义。
PostgreSQL 中的正确写法
PostgreSQL 支持标准语法,且对 PERCENTILE_CONT 兼容性好。要查单个中位数,通常用子查询或 CTE 避免重复行:
SELECT DISTINCT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY score)
FROM students;
如果想按组计算(比如每个班级的中位数),必须用窗口形式:
SELECT
class,
score,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY score) OVER (PARTITION BY class) AS median_score
FROM students;
-
WITHIN GROUP (ORDER BY ...) 是关键语法,不是 OVER (ORDER BY ...)
- 它不接受
PARTITION BY,分区必须靠外层 OVER 或聚合实现
- 若数据含
NULL,默认被忽略(和 ORDER BY 行为一致)
SQL Server 和 Oracle 的差异点
SQL Server 从 2012 开始支持 PERCENTILE_CONT,但必须用窗口语法,不能用 WITHIN GROUP:
SELECT DISTINCT
PERCENTILE_CONT(0.5) OVER (ORDER BY score) AS median_score
FROM students;
Oracle 略复杂:它要求 PERCENTILE_CONT 必须搭配 OVER,且 ORDER BY 列不能有重复值(否则可能报 ORA-30498: argument should be of numeric or date/datetime type)。稳妥做法是加 RN 辅助排序:
SELECT DISTINCT PERCENTILE_CONT(0.5)
OVER (ORDER BY score, rowid) AS median_score
FROM students;
- SQL Server 不支持
WITHIN GROUP,只认 OVER
- Oracle 对
ORDER BY 的稳定性更敏感,建议补上唯一列
- 三者都要求输入列为数值型或可排序类型(如
DATE),传字符串会直接报错
替代方案:为什么有时不用 PERCENTILE_CONT
PERCENTILE_CONT 在大数据量下性能较差,尤其带 PARTITION BY 时,PostgreSQL 可能触发全表排序。生产环境遇到慢查询,常改用:
- 用
ROW_NUMBER() + 计数估算中位位置(快,但对偶数个需手动取均值)
- 用
APPROX_PERCENTILE(Trino/BigQuery)或 PERCENTILE_DISC(返回实际存在的值,非插值)
- 在应用层拉取有序数据后计算,避开数据库插值开销
真正需要“精确中位数”的场景其实不多——多数报表只要稳定、可复现即可。PERCENTILE_CONT 的插值逻辑看似严谨,但一旦底层数据分布偏斜或存在大量重复值,插值得到的数未必比 PERCENTILE_DISC 更有业务意义。
WITHIN GROUP (ORDER BY ...) 是关键语法,不是 OVER (ORDER BY ...)
PARTITION BY,分区必须靠外层 OVER 或聚合实现NULL,默认被忽略(和 ORDER BY 行为一致)PERCENTILE_CONT,但必须用窗口语法,不能用 WITHIN GROUP:
SELECT DISTINCT PERCENTILE_CONT(0.5) OVER (ORDER BY score) AS median_score FROM students;Oracle 略复杂:它要求
PERCENTILE_CONT 必须搭配 OVER,且 ORDER BY 列不能有重复值(否则可能报 ORA-30498: argument should be of numeric or date/datetime type)。稳妥做法是加 RN 辅助排序:
SELECT DISTINCT PERCENTILE_CONT(0.5) OVER (ORDER BY score, rowid) AS median_score FROM students;
- SQL Server 不支持
WITHIN GROUP,只认OVER - Oracle 对
ORDER BY的稳定性更敏感,建议补上唯一列 - 三者都要求输入列为数值型或可排序类型(如
DATE),传字符串会直接报错
替代方案:为什么有时不用 PERCENTILE_CONT
PERCENTILE_CONT 在大数据量下性能较差,尤其带 PARTITION BY 时,PostgreSQL 可能触发全表排序。生产环境遇到慢查询,常改用:
- 用
ROW_NUMBER() + 计数估算中位位置(快,但对偶数个需手动取均值)
- 用
APPROX_PERCENTILE(Trino/BigQuery)或 PERCENTILE_DISC(返回实际存在的值,非插值)
- 在应用层拉取有序数据后计算,避开数据库插值开销
真正需要“精确中位数”的场景其实不多——多数报表只要稳定、可复现即可。PERCENTILE_CONT 的插值逻辑看似严谨,但一旦底层数据分布偏斜或存在大量重复值,插值得到的数未必比 PERCENTILE_DISC 更有业务意义。
ROW_NUMBER() + 计数估算中位位置(快,但对偶数个需手动取均值)APPROX_PERCENTILE(Trino/BigQuery)或 PERCENTILE_DISC(返回实际存在的值,非插值)










