percentile_cont(0.5) 是最通用的中位数计算方式,但必须配合 over() 或 within group 使用;它为窗口函数,非聚合函数,order by 不可省略且字段需支持排序。

PERCENTILE_CONT(0.5) 是最直接的中位数解法,但必须配合 OVER() 使用
SQL 标准里没有 MEDIAN() 聚合函数,PERCENTILE_CONT(0.5) 是目前最通用、语义最清晰的中位数计算方式。但它不是聚合函数,而是**窗口函数**——这意味着你不能写 SELECT PERCENTILE_CONT(0.5) FROM t,必须带 OVER() 子句,否则会报错 ERROR: PERCENTILE_CONT requires a window definition。
常见误用场景:想对整张表求一个中位数值,却漏掉 OVER() 或只写空括号。正确写法是 PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY col)(PostgreSQL/Oracle)或 PERCENTILE_CONT(0.5) OVER (ORDER BY col)(SQL Server),二者语法不同但目的相同。
- PostgreSQL 和 Oracle 支持
WITHIN GROUP形式,可直接用于聚合上下文(如GROUP BY后求每组中位数) - SQL Server 只支持窗口形式,若需单值结果,得套一层
SELECT TOP 1 ... ORDER BY ...或用子查询去重 - MySQL 8.0+ 不支持
PERCENTILE_CONT,得用变量或ROW_NUMBER()手动算位置
ORDER BY 在 OVER() 里不能省,且字段类型要支持排序
PERCENTILE_CONT 的本质是按指定顺序插值,所以 ORDER BY 是强制的。如果写成 PERCENTILE_CONT(0.5) OVER (),所有数据库都会拒绝执行。
容易被忽略的是字段类型兼容性:比如对 TEXT 或 JSON 字段直接 ORDER BY,可能报错或产生意外排序(如按字典序排数字字符串 "10" INT, FLOAT, DECIMAL)或已显式转换:
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY CAST(price AS DECIMAL(10,2)))
- NULL 值默认被忽略(符合中位数定义),无需额外
WHERE col IS NOT NULL - 若排序字段含大量重复值,插值结果仍准确,因为算法基于秩次而非唯一值数量
- 在
GROUP BY场景下,WITHIN GROUP的ORDER BY是局部的,不影响外层排序逻辑
结果可能是小数,即使原始数据全是整数
PERCENTILE_CONT(0.5) 返回的是线性插值结果。当行数为偶数时(如 4 行),它取第 2 和第 3 个值的平均数——哪怕这两值都是整数,结果也会是 DECIMAL 类型。例如数据 [1,3,3,5],中位数是 (3+3)/2 = 3.0,类型通常是 numeric 或 double precision。
这会影响后续比较或导出:如果你写 WHERE median_col = 3,可能因精度不匹配失败;导出到 Excel 时也可能显示为 "3.0" 而非 "3"。
- 需要整数结果时,可用
ROUND(median_col)或FLOOR(median_col + 0.5),但注意四舍五入会改变统计意义 - 更稳妥的做法是保留原精度,在应用层格式化显示
- 对比
PERCENTILE_DISC(0.5):它返回实际存在的某个值(向下取最近秩次),结果必为原始类型,但严格来说不是数学中位数(尤其偶数长度时)
大数据量下性能差异明显,慎用在无索引字段上
PERCENTILE_CONT 需要对参与计算的全部数据排序,时间复杂度接近 O(n log n)。如果在千万级表的未索引字段上直接跑,可能秒变慢查询,甚至触发磁盘临时表。
优化关键点很实在:
- 确保
ORDER BY字段有索引,尤其是和WHERE条件组合时(如WHERE status = 'active' ORDER BY amount,索引应建为(status, amount)) - 避免在
SELECT *中滥用:如果只要中位数,别把其他大字段或 LOB 一起查出来 - 分区表场景下,
PERCENTILE_CONT无法自动下推到分区裁剪,可能全表扫描——此时先UNION ALL各分区中位数再合并,反而更快
真正麻烦的是嵌套窗口或多次调用:同一个查询里对不同字段分别算中位数,数据库通常无法复用排序结果,会各自排序三次。这时候宁可拆成多个 CTE,显式控制中间结果。











