percentile_cont(0.5) 不等于 median(),因前者是通用连续分位函数(线性插值),后者是postgresql 14+新增的专用聚合函数;percentile_cont必须配合within group (order by ...)或over (partition by ... order by ...)使用,不可单独调用。

PERCENTILE_CONT 直接返回中位数,但必须配合 OVER() 子句使用,单独写在 SELECT 列表里会报错(如 ERROR: PERCENTILE_CONT requires a window specification)。
为什么 PERCENTILE_CONT(0.5) 不等于直接用 MEDIAN()?
PostgreSQL 从 14 开始才支持 MEDIAN() 聚合函数,而 PERCENTILE_CONT(0.5) 是更通用的连续分布分位数计算函数,能处理任意百分位(比如 0.25、0.9),且对重复值和空值行为更可控。
关键区别在于:
-
PERCENTILE_CONT基于线性插值:当数据量为偶数时,在中间两个值之间按比例插值;MEDIAN()在 PostgreSQL 14+ 中行为一致,但旧版本只能靠PERCENTILE_CONT模拟 - 它必须搭配
ORDER BY子句(写在OVER里),否则报错PERCENTILE_CONT requires an ORDER BY clause in the window specification - 不能用于普通聚合上下文(如
GROUP BY后直接写),除非套一层窗口——想按分组算中位数,得用PARTITION BY
按业务维度分组计算中位数(如各城市订单金额中位数)
常见错误是把 PERCENTILE_CONT(0.5) OVER (ORDER BY amount) 写在 GROUP BY city 查询里,结果是全量排序后取中位,不是每个城市的中位数。
正确写法必须用 PARTITION BY:
SELECT DISTINCT
city,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount)
OVER (PARTITION BY city) AS median_order_amount
FROM orders;
注意两点:
- PostgreSQL 支持两种语法:
WITHIN GROUP(聚合式,可直接配GROUP BY)和OVER(窗口式,需DISTINCT或子查询去重);前者更直观 - 推荐用
WITHIN GROUP版本,避免窗口函数导致的重复行问题:
SELECT city, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) AS median_order_amount FROM orders GROUP BY city;
NULL 值和性能要注意什么?
PERCENTILE_CONT 默认忽略 NULL,这点和 SUM/AVG 一致,通常符合预期。但如果业务要求把 NULL 当作 0 处理,得显式转换:
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY COALESCE(amount, 0))
性能方面:
- 底层会触发排序,大数据量下比
COUNT+OFFSET手动找中位数还慢一点,但语义清晰、不易出错 - 如果字段没索引,
ORDER BY amount可能成为瓶颈;对高频中位数查询,考虑给关键指标字段加索引 - MySQL 和 SQL Server 不支持
PERCENTILE_CONT,别直接迁移语句——MySQL 8.0+ 用PERCENT_RANK()+ 子查询模拟,SQL Server 用PERCENTILE_CONT但语法相同(注意兼容级别)
真正容易被忽略的是:当你在 CTE 或子查询里用 PERCENTILE_CONT 并希望后续过滤中位数 > X 的分组时,不能直接在 WHERE 里引用该列(窗口函数结果不可在同级 WHERE 中引用),得套一层外层查询。











