SQL无标准MEDIAN()函数,主流数据库普遍不支持;推荐用PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY col)计算中位数,它跨库兼容、语义明确,支持GROUP BY分组;手动实现需ROW_NUMBER()与COUNT()配合处理奇偶逻辑。

SQL里没有标准的MEDIAN()函数,别直接写
几乎所有主流SQL方言(PostgreSQL、SQL Server、Oracle除外)都不原生支持MEDIAN()聚合函数。MySQL 8.0+ 和 SQLite 3.42+ 虽然加了MEDIAN(),但行为不一致:MySQL只接受单列且不支持GROUP BY外的上下文,SQLite的MEDIAN()不能用在窗口函数中。直接写SELECT MEDIAN(salary) FROM emp在 PostgreSQL 或 BigQuery 里会报错function median(numeric) does not exist。
用PERCENTILE_CONT(0.5)是最可靠的标准解法
这是 ANSI SQL 标准定义的连续百分位函数,PostgreSQL、SQL Server、Oracle、BigQuery、Snowflake 都支持,语义明确且结果稳定。它会在排序后做线性插值,对偶数个值自动取中间两数平均值。
实操建议:
-
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary)是聚合用法,等价于“全局中位数” - 必须写
WITHIN GROUP子句,漏掉会语法错误 - 不能和普通列混选而不加
GROUP BY;如果要每部门一个中位数,得配GROUP BY dept_id - 在 PostgreSQL 中,该函数返回
double precision,若原始列为integer,需显式::numeric转换避免截断
示例(PostgreSQL):
SELECT dept_id, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median_salary FROM employees GROUP BY dept_id;
用窗口函数实现“分组内中位数”时,ROW_NUMBER() + COUNT()更可控
当需要为每一行附上其所在分组的中位数值(比如“每个员工薪资 vs 所在部门中位数”),PERCENTILE_CONT() OVER (...)在部分引擎(如 Spark SQL)中受限或不准。这时手动算更稳。
核心思路:对每组排序编号,再根据总数奇偶判断取哪一行或平均哪两行。
关键点:
- 用
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary)编号,不是RANK()(重复值会跳号) - 用
COUNT(*) OVER (PARTITION BY dept_id)得总数n,中位位置是n/2和n/2+1(n为偶)或(n+1)/2(n为奇) - MySQL 8.0+ 可用
LAG()/LEAD()辅助取相邻行,但不如布尔条件+AVG()简洁 - BigQuery 中注意
OFFSET从0开始,ARRAY_AGG(salary ORDER BY salary)[OFFSET(divide(count,2))]仅适用于奇数,偶数需额外处理
简化写法(PostgreSQL / Snowflake):
SELECT
emp_id,
salary,
dept_id,
AVG(salary) OVER (
PARTITION BY dept_id
ORDER BY salary
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) FILTER (WHERE rn IN (n/2 + 1, (n+1)/2)) AS median_salary
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary) AS rn,
COUNT(*) OVER (PARTITION BY dept_id) AS n
FROM employees
) t;
MySQL 和 SQLite 用户绕不开的手动实现
MySQL 8.0+ 虽有MEDIAN(),但它不能用于窗口、不支持GROUP BY混合查询,且文档明确说“不保证与PERCENTILE_CONT结果一致”。SQLite 的MEDIAN()甚至不接受DISTINCT或FILTER。稳妥做法还是回归排序+计数。
MySQL 实操要点:
- 用变量模拟行号(
@row := @row + 1)或ROW_NUMBER()(8.0+) - 嵌套两层查询:外层过滤出中间1或2行,内层算
COUNT和排序 - 避免
LIMIT 1 OFFSET X直接取——它不处理偶数情况下的平均逻辑 - 如果表很大,
ORDER BY ... LIMIT子查询可能无法走索引,记得在ORDER BY字段建索引
一句话结论:真实业务中,只要引擎支持PERCENTILE_CONT,就优先用它;否则手动实现时,重点不是“怎么写出中位数”,而是“怎么让奇偶逻辑不漏 case、不崩类型、不拖慢查询”。尤其注意空值处理——默认ORDER BY会把NULL排最前,而多数场景要ORDER BY salary NULLS LAST。











