PERCENTILE_CONT是窗口函数,必须搭配OVER子句(如OVER(PARTITION BY col1 ORDER BY col2))或WITHIN GROUP语法使用;直接SELECT PERCENTILE_CONT(0.5) FROM t会报错,因其不能脱离计算范围独立执行。

PERCENTILE_CONT 为什么必须搭配 OVER 子句才能用
直接写 SELECT PERCENTILE_CONT(0.5) FROM t 会报错,因为 PERCENTILE_CONT 是一个**窗口函数**,不是聚合函数。它不接受 GROUP BY,也不能单独出现在 SELECT 列表里而不定义计算范围。你必须用 OVER 明确指定排序逻辑和分组边界。
常见错误现象:ERROR: window function PERCENTILE_CONT requires an OVER clause 或结果全为 NULL(因未排序或分区为空)。
- 必须写
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY col)—— 这是标准 SQL 写法,但仅适用于 PostgreSQL 9.4+ 和某些新版数据库;多数场景下仍需OVER - 在 PostgreSQL 中,
PERCENTILE_CONT(0.5) OVER (PARTITION BY group_col ORDER BY val_col)是主流写法,但注意:它会在每行返回一个中位数,不是每组一行 - 要得到“每组一行”的中位数,得配合
DISTINCT ON(PostgreSQL)或外层聚合(如用ROW_NUMBER()做手动中位数)
如何真正拿到每个分组的中位数值(一行一组)
直接套 PERCENTILE_CONT + OVER 会返回重复值——比如某组有 10 行,你就得到 10 行相同中位数。这不是通常要的“汇总结果”。正确做法是先算出中位数,再去重或聚合。
PostgreSQL 示例(推荐):
SELECT DISTINCT ON (category) category, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) AS median_amount FROM sales GROUP BY category;
MySQL 8.0+ 不支持 WITHIN GROUP,只能用窗口函数模拟:
SELECT category, AVG(amount) AS median_amount
FROM (
SELECT category, amount,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY amount) AS rn,
COUNT(*) OVER (PARTITION BY category) AS cnt
FROM sales
) t
WHERE rn IN (FLOOR((cnt + 1) / 2.0), CEIL((cnt + 1) / 2.0))
GROUP BY category;
-
WITHIN GROUP是更简洁、语义更清晰的写法,但仅限支持该语法的数据库(PostgreSQL、Oracle、SQL Server 2022+) - 用
ROW_NUMBER手动实现时,注意偶数行要取中间两值的平均,奇数行取正中那个——FLOOR和CEIL联合覆盖两种情况 - 如果数据含 NULL,默认被忽略;若想把 NULL 当最小值处理,需显式
ORDER BY COALESCE(col, -999999)
PERCENTILE_CONT(0.5) 和 PERCENTILE_DISC(0.5) 的关键区别
两者都算中位数,但行为不同:PERCENTILE_CONT 插值计算(可能返回原数据中不存在的值),PERCENTILE_DISC 取实际存在的离散值(向下取最近的观测值)。
例如数据 [1, 3, 5, 7]:
-
PERCENTILE_CONT(0.5)→4.0((3+5)/2 插值得到) -
PERCENTILE_DISC(0.5)→3(第 2 个值,即FLOOR((4+1)/2)=2位置)
使用场景决定选谁:需要统计学意义的连续中位数(如报表均值对比),用 CONT;要求结果必须来自原始数据(如等级评定、分段阈值),用 DISC。
注意:PERCENTILE_DISC 同样必须配 WITHIN GROUP 或 OVER,不能裸用。
性能和 NULL 处理的隐性坑
大表上跑 PERCENTILE_CONT 很容易慢——它内部要对每个分组做完整排序。如果没索引支撑 PARTITION BY + ORDER BY 字段,性能会断崖式下降。
- 确保组合索引存在:例如
CREATE INDEX idx_category_amount ON sales(category, amount) - NULL 值默认被排除在排序之外,导致中位数计算基数变小;若业务要求包含 NULL(如视 NULL 为 0),需提前转换:
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY COALESCE(amount, 0)) - PostgreSQL 中,
PERCENTILE_CONT在GROUP BY后使用WITHIN GROUP比嵌套窗口函数快得多,优先选前者
最常被忽略的是:没有验证分组内是否真有数据。空分组会导致 PERCENTILE_CONT 返回 NULL,且不会报错——得靠 HAVING COUNT(*) > 0 或外层过滤兜底。

















