percentile_cont是sql标准定义的连续分布百分位函数,通过线性插值计算第50百分位(即中位数),在偶数行时返回中间两值平均值,严格符合统计定义;它需配合within group(order by ...)或over(partition by ... order by ...)使用,postgresql、sql server、oracle、bigquery支持,mysql和sqlite不支持。

PERCENTILE_CONT 是什么,为什么它能算中位数
PERCENTILE_CONT 是 SQL 标准中定义的连续分布百分位函数,不是所有数据库都支持——PostgreSQL 9.4+、SQL Server 2012+、Oracle、BigQuery 支持;MySQL 和 SQLite 原生不支持。它的核心行为是:在排序后的值之间做线性插值,所以当请求 0.5(即中位数)时,它会返回第 50% 位置的“理论值”,哪怕该位置落在两个实际行之间。
这和 PERCENTILE_DISC 不同:PERCENTILE_DISC(0.5) 直接取排序后靠前的那个实际值(向下取整式),而 PERCENTILE_CONT(0.5) 可能返回平均值,更符合数学中位数定义。
基本语法与 GROUP BY 配合写法
直接对分组数据求中位数,必须用窗口函数 + GROUP BY 组合,不能只靠 OVER (PARTITION BY ...) 就完事——因为 PERCENTILE_CONT 是聚合函数(尽管写法像窗口函数),需配合 GROUP BY 或子查询。
常见正确结构:
SELECT category, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY value) AS median_value FROM sales GROUP BY category;
注意点:
-
WITHIN GROUP (ORDER BY value)是强制语法,不能写成OVER (ORDER BY value) -
value列必须是数值类型,否则报错:ERROR: argument of PERCENTILE_CONT must be numeric - NULL 值默认被忽略,无需额外
WHERE value IS NOT NULL,但显式过滤更可控
不同数据库的兼容性陷阱
PostgreSQL 和 SQL Server 行为一致,但 Oracle 要求指定 RESPECT NULLS(虽默认就是),而 BigQuery 把它叫 PERCENTILE_CONT(value, 0.5),且只能用于分析函数模式(OVER),不支持 WITHIN GROUP。
典型错误场景:
- 在 MySQL 执行
PERCENTILE_CONT→ 直接报错Unknown function 'PERCENTILE_CONT' - 在 BigQuery 写
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY x)→ 语法错误,必须改用PERCENTILE_CONT(x, 0.5) OVER (PARTITION BY group_col)并配合去重逻辑 - PostgreSQL 中若
value是TEXT类型,会提示cannot order TEXT values,需先::NUMERIC转换
替代方案:当 PERCENTILE_CONT 不可用时怎么办
在 MySQL 或旧版 SQL Server 中,得手动模拟。最稳的通用做法是用行号定位中间位置:
SELECT category, AVG(value) AS median_value
FROM (
SELECT category, value,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY value) 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;
这个逻辑覆盖奇偶两种情况,但要注意:
-
FLOOR和CEIL在不同数据库里函数名可能不同(如 SQL Server 用ROUND(..., 0, 1)模拟 FLOOR) - 如果存在大量重复值,仍能正确返回中位数,但性能比
PERCENTILE_CONT差一截,尤其数据量大时 - 别漏掉外层
GROUP BY和AVG,否则偶数长度组会返回两行
真正麻烦的从来不是怎么写,而是确认你用的数据库版本是否支持、以及 NULL 和类型隐式转换有没有悄悄改变结果。











