sql server 2022起支持percentile_cont(0.5)窗口函数按部门计算中位数,2019及更早需用row_number()+count()手动实现;两者均默认忽略null,重复值无需去重,但需注意排序稳定性。

SQL Server 中位数不能直接用 MEDIAN() 函数
SQL Server 直到 2022 版本才原生支持 MEDIAN() 窗口函数,且仅限于 DISCRETE 模式(返回实际存在的值),CONTINUOUS 模式(线性插值)仍不支持。如果你用的是 SQL Server 2019 或更早版本,必须手动实现——这不是语法糖问题,而是底层缺失聚合语义。
常见错误是试图用 AVG() 或 PERCENTILE_CONT(0.5) 混搭窗口与分组逻辑,结果要么报错 “Windowed functions cannot be used in the context of another windowed function”,要么在 GROUP BY 后丢失部门粒度。
用 PERCENTILE_CONT(0.5) OVER (PARTITION BY dept) 是最简方案(2022+)
SQL Server 2022 开始,PERCENTILE_CONT(0.5) 支持作为窗口函数使用,且能正确配合 PARTITION BY 按部门计算中位数。它返回连续型中位数(即两个中间值的平均值),符合统计惯例。
-
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) OVER (PARTITION BY dept)—— 注意:WITHIN GROUP必须存在,且ORDER BY不可省略 - 该表达式不能出现在
WHERE或HAVING中,只能用于SELECT或ORDER BY - 若某部门只有 1 行数据,结果就是该行 salary;若为偶数行(如 4 行),则取第 2 和第 3 行 salary 的平均值
- 性能上,它会触发排序操作,大数据量时建议在
(dept, salary)上建复合索引
SQL Server 2019 及更早:用 ROW_NUMBER() + COUNT() 手动模拟
核心思路是给每部门薪资排序编号,再根据总行数奇偶性决定取一个值还是两个值的平均。关键陷阱在于:不能只写 ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary) 就完事——你还需要知道每个部门总人数,而 COUNT(*) OVER (PARTITION BY dept) 必须和排序在同一查询层级,否则窗口帧不一致。
- 先算出每个员工在部门内的序号:
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary) - 再算出部门总人数:
COUNT(*) OVER (PARTITION BY dept) - 用子查询或 CTE 包裹后,在外层用条件判断:
WHERE rn IN ((cnt + 1) / 2, (cnt + 2) / 2)—— 这个技巧兼容奇偶:当 cnt=5 → (6/2,7/2)=(3,3);cnt=4 → (5/2,6/2)=(2,3),整数除法在 SQL Server 中自动截断 - 最后用
AVG(salary)聚合这两行(即使只有一行,AVG也不影响结果)
WITH ranked AS (
SELECT dept, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary) AS rn,
COUNT(*) OVER (PARTITION BY dept) AS cnt
FROM employees
)
SELECT dept, AVG(1.0 * salary) AS median_salary
FROM ranked
WHERE rn IN ((cnt + 1) / 2, (cnt + 2) / 2)
GROUP BY dept;
NULL 值和重复薪资会影响结果,必须提前处理
中位数对 NULL 敏感:PERCENTILE_CONT 和 ROW_NUMBER() 默认都跳过 NULL,但如果你业务要求把 NULL 当作最低值参与排序,就得显式用 ORDER BY ISNULL(salary, 0) ASC 或补默认值。更危险的是重复薪资——它们会获得不同 ROW_NUMBER,但实际中位数定义不依赖唯一性,所以无需去重;强行 DISTINCT 反而破坏分布。
- 确认业务规则:NULL 是无效数据(应过滤)?还是代表“未定薪”(需纳入)?
- 若用
PERCENTILE_CONT,它自动忽略 NULL;若用手工法,ROW_NUMBER()同样跳过,但你可以加WHERE salary IS NOT NULL显式控制 - 重复值不用特殊处理,但要注意:若所有薪资相同,中位数就是那个值,手工法中多行可能同时命中
rn IN (...)条件,AVG依然正确
真正容易被忽略的是排序稳定性:SQL Server 在 ORDER BY salary 相同时不保证行序,可能导致同一查询多次执行结果微异(尤其并行计划下)。加个二级排序字段,比如 ORDER BY salary, emp_id,能彻底避免。










