PERCENTILE_CONT 是有序集合聚合函数,必须用 WITHIN GROUP (ORDER BY ...) 指定排序,参数为 0.0–1.0 的分位点,支持聚合和窗口用法,与 PERCENTILE_DISC 的插值逻辑不同。

PostgreSQL 的 PERCENTILE_CONT 能直接算连续分位数,但必须配合 ORDER BY 和窗口或聚合用法,否则报错或结果错得离谱。
为什么 PERCENTILE_CONT 一定要写 WITHIN GROUP (ORDER BY ...)
这个函数不是普通标量函数,它属于“有序集合聚合函数”——意思是它不关心整张表怎么排,只关心你喂给它的那一组值按什么顺序排。漏掉 WITHIN GROUP 会直接报错:ERROR: PERCENTILE_CONT requires an ORDER BY clause。
常见误写:
SELECT PERCENTILE_CONT(0.5) FROM sales;
正确写法(计算全表中 amount 的中位数):
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) FROM sales;
- 参数
0.5是分位点,取值范围必须是0.0到1.0(含端点),传50或'50%'会报错 - 如果
amount全为NULL,结果也是NULL,不会报错但需提前处理空值 - 排序字段类型必须支持比较(如
numeric、timestamp),对jsonb或未实现<的自定义类型会失败
想按组分别算分位数?别用 GROUP BY,要用窗口函数
用 GROUP BY 配合 PERCENTILE_CONT 只能算每组的单个分位值;如果想在每行上同时显示本组的中位数、90 分位数等,必须改用窗口函数写法。
错误示范(只能返回一行 per group):
SELECT region, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) FROM sales GROUP BY region;
正确窗口写法(每行都带本 region 的中位数):
SELECT region, amount,<br> PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) OVER (PARTITION BY region) AS median_amount<br>FROM sales;
-
OVER (PARTITION BY region)决定了“每组内部排序并插值”,和GROUP BY语义不同 - 窗口版本不支持多个分位点一次写多个
WITHIN GROUP,要算中位数和 90 分位就得写两列:PERCENTILE_CONT(0.5) ...和PERCENTILE_CONT(0.9) ... - 注意:窗口函数里的
ORDER BY在WITHIN GROUP里写,不是在OVER里写;OVER (... ORDER BY ...)是给整个窗口排序,和分位数无关
PERCENTILE_CONT 和 PERCENTILE_DISC 到底选哪个
两者都算分位数,但插值逻辑完全不同,结果可能差很大——尤其样本少时。
PERCENTILE_CONT 做线性插值:比如 4 个数 [10,20,30,40] 算 0.25 分位,位置是 1 + (4-1)*0.25 = 1.75,取第 1 个(10)和第 2 个(20)之间 0.75 处,结果是 17.5。
PERCENTILE_DISC 只返回实际存在的值:同样数据算 0.25 分位,向下取整到第 1 个位置,结果就是 10。
- 需要平滑连续估值(如做统计建模、画分布曲线)→ 用
PERCENTILE_CONT - 需要返回真实观测值(如“前 25% 用户的最低消费额”这种业务口径)→ 用
PERCENTILE_DISC - 两者对
NULL的处理一致:默认忽略,但若整组全NULL,结果都是NULL
真正容易被忽略的是:当分位点落在两个相等的值之间时,PERCENTILE_CONT 仍会插值得出中间值,而业务方有时默认“等于分位阈值就算达标”,这时候数值本身没意义,得配合 CASE 或 NTILE 再判断。

















