在SQL数据分析工作中,经常需要按指定维度分组后计算组内数据的中位数,中位数是排序后位于中间位置的数据值,比平均值更能体现数据的典型水平,避免极端值干扰。PERCENTILE_CONT是SQL标准中定义的窗口函数,专门用于计算连续分布的百分位数,当传入参数0.5时,就可以直接得到分组内的中位数,无需手动排序后取中间值。

PERCENTILE_CONT函数基础语法
PERCENTILE_CONT函数的标准语法结构如下:
PERCENTILE_CONT(百分位数值) WITHIN GROUP (ORDER BY 排序字段) OVER (PARTITION BY 分组字段)
其中各个参数的含义为:
- 百分位数值:取值范围是0到1之间的小数,0.5代表中位数,0代表最小值,1代表最大值
- WITHIN GROUP (ORDER BY 排序字段):指定计算百分位数时的排序规则,必须指定排序字段
- OVER (PARTITION BY 分组字段):指定分组的维度,如果不写PARTITION BY则对全表数据计算
单分组场景下的中位数计算
假设我们有一张员工工资表employee_salary,包含员工ID、部门ID、工资三个字段,现在需要计算全公司员工工资的中位数,不需要分组,实现代码如下:
-- 创建测试表
CREATE TABLE employee_salary (
emp_id INT,
dept_id INT,
salary DECIMAL(10,2)
);
-- 插入测试数据
INSERT INTO employee_salary VALUES
(1, 1, 8000),
(2, 1, 9500),
(3, 1, 12000),
(4, 2, 7000),
(5, 2, 8500),
(6, 2, 10000),
(7, 3, 9000),
(8, 3, 11000);
-- 计算全公司工资中位数
SELECT
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) OVER () AS total_median_salary
FROM employee_salary
LIMIT 1;
上述查询中,OVER()后面没有PARTITION BY子句,所以是对全表所有工资数据排序后计算0.5分位数,也就是中位数。由于窗口函数会对每一行都返回结果,所以使用LIMIT 1只取一条结果即可。
多分组场景下的分组内中位数计算
如果需要按部门分组,计算每个部门内部的工资中位数,只需要在OVER子句中添加PARTITION BY dept_id即可,代码如下:
-- 计算每个部门的工资中位数
SELECT
dept_id,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) OVER (PARTITION BY dept_id) AS dept_median_salary
FROM employee_salary
GROUP BY dept_id, salary;
如果希望每个部门只返回一条中位数结果,可以结合子查询去重:
SELECT DISTINCT
dept_id,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) OVER (PARTITION BY dept_id) AS dept_median_salary
FROM employee_salary;
查询结果会分别返回部门1、部门2、部门3各自的工资中位数,部门1的三条工资排序后是8000、9500、12000,中位数是9500;部门2的三条工资排序后是7000、8500、10000,中位数是8500;部门3的两条工资排序后是9000、11000,中位数是(9000+11000)/2=10000。
不同数据库的使用差异说明
PERCENTILE_CONT是SQL标准函数,大部分主流数据库都支持,但部分细节有差异:
- MySQL 8.0及以上版本支持该函数,语法与标准一致
- PostgreSQL从9.4版本开始支持,语法无差异
- Oracle支持该函数,同时还可以使用PERCENTILE_DISC函数,后者返回离散分布的百分位数,当数据个数为偶数时,PERCENTILE_CONT会取中间两个值的平均值,PERCENTILE_DISC会取排序后靠前的那个值
- SQL Server 2012及以上版本支持,语法一致
与传统实现方式的对比
在PERCENTILE_CONT函数出现之前,计算分组内中位数通常需要先排序,再根据数据总数的奇偶性取中间值,实现复杂度高,比如传统方式计算部门工资中位数的代码如下:
WITH salary_rank AS (
SELECT
dept_id,
salary,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary) AS rn,
COUNT(*) OVER (PARTITION BY dept_id) AS total_cnt
FROM employee_salary
)
SELECT
dept_id,
AVG(salary) AS dept_median_salary
FROM salary_rank
WHERE rn IN (FLOOR((total_cnt + 1)/2), CEIL((total_cnt + 1)/2))
GROUP BY dept_id;
对比可见,使用PERCENTILE_CONT函数只需要一行核心代码就可以实现,逻辑更清晰,执行效率也更高,不需要多次扫描表和复杂的排序判断。
注意事项
- PERCENTILE_CONT计算的是连续百分位数,当数据个数为偶数时,会返回中间两个值的平均值,符合中位数的通用计算规则
- 排序字段如果有重复值,函数会自动处理重复情况,不需要额外去重
- 如果分组内只有一条数据,中位数就是该数据本身
- 百分位数值必须是0到1之间的小数,超出范围会报错
SQL中位数PERCENTILE_CONT窗口函数分组计算修改时间:2026-07-02 06:12:12