sql server 2012+中percentile_cont(0.5)是窗口函数,非聚合函数,故不能与group by混用;正确写法为percentile_cont(0.5) over (partition by dept order by salary),否则报语法错误。

SQL Server 2012+ 可以直接用 PERCENTILE_CONT(0.5) 算分组中位数,但必须用窗口语法 OVER (PARTITION BY ... ORDER BY ...),不能和 GROUP BY 混用——否则报错“Incorrect syntax near the keyword 'OVER'”或“must be used with OVER clause”。
为什么 PERCENTILE_CONT + GROUP BY 会报错
因为 PERCENTILE_CONT 是窗口函数,不是聚合函数。它不压缩行,而是为每行输出一个计算值;而 GROUP BY 要求所有非分组列都得是聚合结果(如 SUM()、AVG())。两者语义冲突。
常见错误写法:
SELECT dept, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) FROM employees GROUP BY dept
这在 SQL Server 中非法:WITHIN GROUP 不能和 GROUP BY 共存。PostgreSQL 支持这种聚合写法,但 SQL Server 不支持。
- 正确路径只有一条:用
OVER定义窗口,不写GROUP BY - 如果只要每组一个值(比如报表汇总),需配合
DISTINCT或子查询去重 - 如果想让每行都带本组中位数(如做离差分析),
OVER写法天然支持,无需额外处理
PERCENTILE_CONT(0.5) 的正确窗口写法
核心结构固定:PERCENTILE_CONT(0.5) OVER (PARTITION BY group_col ORDER BY sort_col)。SQL Server 强制要求 ORDER BY 存在,且不允许省略窗口框架(哪怕你想全组计算,也得显式写)。
示例(按部门算薪资中位数,并附在每行上):
SELECT employee_id, dept, salary, PERCENTILE_CONT(0.5) OVER (PARTITION BY dept ORDER BY salary) AS median_salary FROM employees;
-
PARTITION BY dept控制分组粒度,可多列,如PARTITION BY dept, year -
ORDER BY salary必须是非空、可排序列;若salary含NULL,SQL Server 默认跳过,但结果可能偏离业务预期 - 不加
ROWS BETWEEN ...时,SQL Server 默认使用RANGE UNBOUNDED PRECEDING,对中位数无实质影响,但显式写更稳妥
NULL 值和数据类型容易踩的坑
PERCENTILE_CONT 对 NULL 静默忽略——既不报错,也不警告。例如某部门 10 行记录中有 4 行 salary IS NULL,它只对剩下 6 个非空值排序插值。这符合统计惯例,但业务上可能出问题:
- 如果
NULL表示“未发薪”,那忽略是对的;如果表示“底薪为 0”,那结果就偏高 - 想把
NULL当 0 处理?用COALESCE(salary, 0)替换salary,但注意COALESCE不能出现在ORDER BY的WITHIN GROUP形式里——窗口形式可以 - 输入是
INT,结果可能是DECIMAL(18,6),小数位可能干扰下游判断,必要时用ROUND(..., 2)或CAST(... AS INT) - datetime 类型支持良好,但
datetime2在毫秒级数据上可能有 ±1ms 插值偏差,敏感场景建议先转为INT时间戳再算
替代方案:不用 PERCENTILE_CONT 怎么办
如果你用的是 SQL Server 2005–2008 R2,或需要避免多次排序开销(比如在存储过程中反复调用),手动实现更可控:
WITH ranked AS (
SELECT
value,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY value) AS rn,
COUNT(*) OVER (PARTITION BY dept) AS cnt
FROM employees
WHERE value IS NOT NULL
)
SELECT
dept,
AVG(0.0 + value) AS median
FROM ranked
WHERE rn IN ((cnt + 1) / 2, (cnt + 2) / 2)
GROUP BY dept;
-
(cnt + 1) / 2和(cnt + 2) / 2自动覆盖奇偶:奇数时两值相等,偶数时取中间两个位置 -
AVG(0.0 + value)强制转浮点,避免整数除法截断 - 这个模式兼容 SQL Server 2005+,且 CTE 中排序只做一次,比多个
PERCENTILE_CONT更省资源
真正麻烦的不是语法,而是确认你算的到底是“在职员工薪资中位数”,还是“含未入职人员的全体中位数”——NULL 处理方式和业务定义是否对齐,这点比函数选型更重要。











