mysql 8.0+ 不支持 median()、percentile_cont 或 percentile_disc 窗口函数;postgresql 需用 percentile_cont(0.5) within group (order by salary) 配合 group by;sql server 支持窗口形式但需处理 null 和排序稳定性;通用方案是用 row_number 和 count 结合 floor/ceiling 定位中位位置并取 avg。

MySQL 8.0+ 没有 MEDIAN() 窗口函数,别白试了
MySQL 直到 8.0 都不支持 MEDIAN(),也不支持 PERCENTILE_CONT 或 PERCENTILE_DISC 这类标准窗口函数。你执行 SELECT MEDIAN(salary) OVER (PARTITION BY dept) 会直接报错:FUNCTION database.MEDIAN does not exist。PostgreSQL 和 SQL Server 支持,但 MySQL 用户得绕路。
PostgreSQL 中用 PERCENTILE_CONT 计算组内中位数最稳妥
PERCENTILE_CONT(0.5) 是 ANSI 标准的连续中位数计算,对偶数个值会插值(比如 [1,3] 返回 2.0),适合数值型字段;PERCENTILE_DISC(0.5) 则返回实际存在的值([1,3] 返回 1 或 3,取决于排序位置),更接近传统“取中间那个”的直觉。
- 必须配合
ORDER BY子句,否则语法错误:PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) - 不能直接写成窗口函数形式(如
OVER (PARTITION BY dept ORDER BY salary))——PostgreSQL 不允许在WITHIN GROUP同时用OVER,得用子查询或 CTE 拆开 - 示例:按部门算中位薪资
SELECT dept,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median_salary
FROM employees
GROUP BY dept;
SQL Server 的 PERCENTILE_CONT 要注意 NULL 和排序方向
SQL Server 支持 PERCENTILE_CONT(0.5) OVER (PARTITION BY dept ORDER BY salary) 这种真正的窗口写法,但有几个硬坑:
-
ORDER BY列含NULL时,默认排在最前,会导致中位数偏移;建议显式写ORDER BY salary ASC NULLS LAST(如果版本 ≥ 2022)或先过滤/替换NULL - 排序必须是确定性的,否则相同值多时结果可能不稳定;必要时加二级排序,如
ORDER BY salary, id - 该函数返回
float类型,若原字段是int,需用CAST(... AS INT)或ROUND(..., 0)处理,否则小数位可能干扰业务逻辑
通用兼容方案:用 ROW_NUMBER 做双排名模拟中位数
这个方法在 MySQL、PostgreSQL、SQL Server、Oracle 全都能跑,原理是给每组数据按顺序编号,再找中间位置的行。关键点不在“怎么写”,而在“怎么判奇偶”和“怎么防越界”:
- 先算每组总行数:
COUNT(*) OVER (PARTITION BY dept) - 再算正向序号:
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary) - 中位数位置是
(cnt + 1) / 2(奇数)或cnt / 2和cnt / 2 + 1(偶数);用FLOOR和CEILING统一处理更安全 - 别漏掉
WHERE rn IN (FLOOR((cnt+1)/2.0), CEILING((cnt+1)/2.0))—— 这个表达式能同时覆盖奇偶两种情况 - 最后用
AVG()聚合,避免手动UNION或CASE分支
真正容易被忽略的是:当组内所有值相等(比如全是 5000),ROW_NUMBER 仍会严格按顺序编号,不影响中位数结果;但若排序字段有大量重复且没加二级键,ROW_NUMBER 的分配可能因优化器而波动——生产环境务必加唯一辅助排序字段。










