Oracle 19c中KEEP必须与MAX/MIN等聚合函数配合使用,且仅限GROUP BY查询;语法为AGG_FUNC(col) KEEP (DENSE_RANK FIRST|LAST ORDER BY sort_col [DESC|ASC]),用于获取极值所在行的指定列值。

Oracle 19c 中 KEEP 配合 MAX/MIN 的真实写法
KEEP 不是独立函数,而是修饰聚合函数的语法结构,必须和 MAX、MIN 等搭配使用,且只能出现在 GROUP BY 查询中。它解决的核心问题是:“每个分组里极值所在行的其他字段值是多少?”——这正是纯 MAX() 无法回答的。
正确语法骨架是:AGG_FUNC(keep_col) KEEP (DENSE_RANK FIRST|LAST ORDER BY sort_col [DESC|ASC])
-
FIRST表示取排序后排第一的那行对应的keep_col值;LAST同理 -
DENSE_RANK是强制要求的关键字,不能省略或换成RANK或ROW_NUMBER -
ORDER BY必须明确指定方向:要最大值对应行就用DESC,最小值就用ASC - 如果
sort_col有重复(比如多人同薪),DENSE_RANK FIRST会选出所有并列第一的行,但最终MAX(keep_col)会在这些行中再取一个确定值(如字符串最大、数字最大)
为什么用 MAX(name) 而不是直接 name?
因为 Oracle 要求 KEEP 子句中的聚合函数必须返回单个确定值。你不能写 name KEEP (...)——语法不合法。必须套一层 MAX() 或 MIN()。
这个外层聚合的作用不是“找最大名字”,而是从所有并列极值行中选一个代表:
- 用
MAX(name):当多人同为最高薪时,返回这些人中按字典序最大的姓名 - 用
MIN(name):则返回字典序最小的姓名 - 若想按主键选(比如取
employee_id最小的那条),就写MIN(employee_id) KEEP (...) - 注意:这里
MAX和MIN的语义已脱离“数值大小”,转为“确定性选取策略”
常见报错与绕过陷阱
ORA-00979 错误最常出现在试图把 KEEP 写在没 GROUP BY 的查询里,或者漏掉 DENSE_RANK 关键字。
- 错误写法:
SELECT MAX(name) KEEP (FIRST ORDER BY salary DESC) FROM employees→ 缺少GROUP BY,报 ORA-00979 - 错误写法:
SELECT MAX(name) KEEP (FIRST ORDER BY salary DESC) FROM employees GROUP BY department_id→ 缺DENSE_RANK,报 ORA-00904 “invalid identifier” - 别指望
KEEP支持OVER():它只用于聚合上下文,不支持窗口函数语法 - 性能上,只要
ORDER BY字段有索引(如(department_id, salary)复合索引),Oracle 19c 能高效定位每组极值位置,无需全表扫描
替代方案对比:KEEP vs 窗口函数
在 Oracle 19c 中,KEEP 和 ROW_NUMBER() OVER(...) 都能实现“分组取极值行”,但行为不同:
-
KEEP是聚合结果,一行一部门,天然去重;ROW_NUMBER()是逐行编号,需额外WHERE rn = 1过滤 - 当存在薪资并列时:
KEEP+MAX(name)强制返回一个名字;ROW_NUMBER()会随机给序号(除非加ORDER BY employee_id显式稳定排序) - 若业务要求“返回所有并列最高薪员工”,
KEEP不适用,必须换RANK()或DENSE_RANK()窗口函数 -
KEEP不能获取多列(如同时要name和hire_date),得写两遍表达式;窗口函数可一次查出整行
真正容易被忽略的是:KEEP 的语义重心不在“排序”,而在“从排序结果中收缩出单值”——它本质是聚合函数的增强语法,不是窗口逻辑的简化版。


















