percentile_cont(0.5)是标准sql中计算中位数最直接可靠的方式,基于线性插值:偶数行取中间两值平均,奇数行取正中原始值;必须配合within group(order by ...)或over(partition by ... order by ...)使用,不支持mysql/sqlite。

PERCENTILE_CONT(0.5) 是计算中位数最直接的写法
在支持窗口函数的标准 SQL(如 PostgreSQL、SQL Server、Oracle、BigQuery)中,PERCENTILE_CONT(0.5) 就是专为中位数设计的——它做的是连续插值,对偶数个值自动取中间两数的平均值,行为符合统计学定义。别用 PERCENTILE_DISC(0.5),它只返回实际存在的某个值,偶数长度时会向下取舍,结果偏移明显。
常见错误是漏写 WITHIN GROUP (ORDER BY ...) 子句:PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY sales_amount) 才合法;单独写 PERCENTILE_CONT(0.5) 会报错,比如 PostgreSQL 提示 function percentile_cont(double precision) does not exist。
必须配合 OVER() 或 GROUP BY 使用,不能当普通聚合函数直用
PERCENTILE_CONT 是窗口函数,不是传统聚合函数。想算全表中位数,得套一层子查询或 CTE;想按城市分组算各城市中位数,必须加 OVER (PARTITION BY city)。
- ✅ 正确(全表中位数):
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY duration) AS median_duration FROM events;
- ✅ 正确(分组中位数):
SELECT city, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY duration) OVER (PARTITION BY city) AS median_duration FROM events;
- ❌ 错误(无 ORDER BY、无 OVER):
SELECT PERCENTILE_CONT(0.5) FROM events;
NULL 值默认被忽略,但需确认业务是否允许丢弃
PERCENTILE_CONT 默认跳过 NULL 值,这点和 COUNT(*) 不同。如果字段里有 20% 的 NULL,实际参与排序的只有 80% 的非空记录——中位数基于这 80% 算出,可能掩盖数据缺失问题。
业务上常需要明确处理逻辑:
- 若
NULL代表“未发生”,应先过滤:WHERE duration IS NOT NULL - 若
NULL是有效状态(如“未完成”),可考虑用COALESCE(duration, 0)替代,但要同步说明 0 是否合理 - 某些场景需保留
NULL并单独统计比例,这时中位数就得拆成两步:先算非空样本量,再判断是否足够支撑稳健估计
PostgreSQL 和 BigQuery 行为一致,MySQL 目前不支持
MySQL 8.0+ 支持窗口函数,但至今没实现 PERCENTILE_CONT(截至 8.4)。强行要用,只能手写逻辑:用 ROW_NUMBER() + COUNT() 定位中间位置,再用条件聚合取值——代码冗长且易错。
替代方案优先级建议:
- 能切到 PostgreSQL / BigQuery:直接用
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY ...) - stuck on MySQL:用
APPROX_PERCENTILE(..., 0.5)(BigQuery)或PERCENTILE_CONT等价物(如 Redshift 的PERCENTILE_CONT)迁移数据再算 - 实在不行才写 ROW_NUMBER 版本,但务必在注释里标清“此中位数为近似值,未插值”
插值本身不难理解,难的是不同系统对“排序后第 n 个位置”的索引约定(从 1 还是 0 开始)、以及小数位截断方式——这些细节在跨平台比对指标时最容易翻车。










