能,MEDIAN函数在Oracle中可直接用于分组中位数计算,需配合GROUP BY使用,每组返回一行结果;空值自动忽略,偶数个值取中间两数平均;但SELECT中不可混用非聚合列,否则报ORA-00937。

MEDIAN 函数在 Oracle 中能直接完成中位数聚合,但必须注意它只接受单列、忽略 NULL、且不支持 WHERE 条件下提前过滤后再聚合 —— 想筛完再算中位数,得用子查询或 CTE 包一层。
为什么 SELECT MEDIAN(salary) FROM emp WHERE deptno = 10 有时不准?
因为 MEDIAN 是聚合函数,执行顺序在 WHERE 之后、GROUP BY 之前,表面看没问题。但容易踩的坑是:如果 salary 列本身含大量 NULL,而业务上又把 NULL 当作“未发薪”而非“0”,那 MEDIAN 会自动跳过这些行 —— 结果看似合理,实则统计口径和业务理解脱节。
- 确认 NULL 含义:先跑
SELECT COUNT(*), COUNT(salary) FROM emp WHERE deptno = 10,看非空行占比 - 强制补零(慎用):若业务允许,可写
MEDIAN(NVL(salary, 0)),但中位数会被拉低,尤其当 NULL 占比高时 - 更稳妥做法:用
WITH filtered AS (SELECT salary FROM emp WHERE deptno = 10 AND salary IS NOT NULL) SELECT MEDIAN(salary) FROM filtered
MEDIAN 与 GROUP BY 配合时,分组字段必须出现在 SELECT 列表里吗?
必须。Oracle 要求所有非聚合字段都得显式出现在 GROUP BY 子句中,否则报错 ORA-00979: not a GROUP BY expression。不像 MySQL 有些模式允许“隐式分组”。
- 正确写法:
SELECT department_id, MEDIAN(salary) FROM employees GROUP BY department_id - 错误写法:
SELECT dept_name, department_id, MEDIAN(salary) FROM employees GROUP BY department_id(dept_name没进 GROUP BY,也不在聚合内) - 如果
dept_name和department_id是一对一关系,可用MAX(dept_name)或MIN(dept_name)绕过,但本质是借聚合“带出”确定值
MEDIAN OVER(PARTITION BY ...) 的性能代价比 GROUP BY 高在哪?
核心差异在排序粒度:GROUP BY 对全量数据按分组键做一次哈希或排序;而 MEDIAN() OVER(PARTITION BY dept_id) 会为每个 dept_id 值单独执行一次内部排序 —— 分区越多、分区大小越不均,CPU 和临时表空间压力越大。
- 查 100 个部门?可能触发 100 次独立排序
- 某部门有 80 万员工,其他部门平均才 200 人?那个大分区会拖慢整条 SQL
- 避免 SELECT *:如果原表有
CLOB或长文本字段,它们会被复制进窗口结果集,徒增 buffer cache 压力 - 替代思路:先
GROUP BY算好中位数存中间表,再 JOIN 回原表 —— 多一次 I/O,但省下重复排序
大数据量下 MEDIAN 计算变慢,除了加索引还能做什么?
索引对 MEDIAN 效果有限,因为它最终仍要扫描所有非空值并排序。真正起效的是控制输入规模和缓存结果。
- 别在实时报表里硬算:对日活百万级的订单表,每次查
MEDIAN(order_amount),建议改用物化视图每日刷新 - 抽样可行但需谨慎:Oracle 支持
TABLESAMPLE,如SELECT MEDIAN(amount) FROM orders SAMPLE(1)(抽 1%),但中位数对分布敏感,小样本偏差可能极大 - 预计算字段:在 ETL 阶段就存好各维度中位数到汇总表,查询时直接读,不现场算
- 注意隐式类型转换:如果
salary是NUMBER(10,2),但写成MEDIAN(TO_CHAR(salary)),会触发全表转字符串再排序,彻底崩性能
最常被忽略的一点:MEDIAN 的结果类型取决于输入列。如果源列是 DECIMAL,结果仍是 DECIMAL;如果是 NUMBER,结果默认为 NUMBER,但精度可能丢失 —— 比如原始值带 4 位小数,MEDIAN 返回却只有 2 位,这时候得套一层 ROUND(MEDIAN(col), 4) 显式控制。


















