应使用子查询排除最大值再取MAX,即SELECT MAX(salary) FROM employee WHERE salary < (SELECT MAX(salary) FROM employee),该写法兼容所有主流SQL方言、语义清晰且天然规避重复值问题。

用 LIMIT 和 OFFSET 跳过最高值
最直接的办法是先按薪资降序,再跳过第 1 名取第 2 名。但要注意:如果最高薪资有重复(比如两人都是 15000),LIMIT 1 OFFSET 1 会返回并列第二高的那个值,而不是“严格第二高”的值。
实操建议:
- 仅适用于 MySQL / PostgreSQL,SQLite 也支持;SQL Server 和 Oracle 不支持
OFFSET语法 - 写法:
SELECT DISTINCT salary FROM employee ORDER BY salary DESC LIMIT 1 OFFSET 1 - 必须加
DISTINCT,否则重复最高值会导致偏移错位 - 如果全表只有一条记录,这条语句返回空结果——不是报错,容易被忽略
用子查询排除最大值再取 MAX
这是兼容性最好、语义最清晰的写法:先查出最高薪资,再查“小于该值的最大薪资”。它天然避开重复值干扰,且所有主流 SQL 方言都支持。
实操建议:
- 写法:
SELECT MAX(salary) FROM employee WHERE salary - 如果所有人薪资相同,内层
MAX和外层WHERE条件不成立,结果为NULL - 不能用
NOT IN替代(比如salary NOT IN (SELECT MAX(...))),因为若子查询返回NULL,整个条件恒为UNKNOWN,结果为空 - 性能上,两次扫描表,但优化器通常能复用索引,实际影响不大
用窗口函数 ROW_NUMBER() 排名取第 2
当需要“并列不占位”的排名(比如两个第一、第三是真正第二高)时,ROW_NUMBER() 比 RANK() 更符合“第二高”的字面意思——它强制编号,不跳过数字。
实操建议:
- 写法:
SELECT salary FROM (SELECT salary, ROW_NUMBER() OVER (ORDER BY salary DESC) AS rn FROM (SELECT DISTINCT salary FROM employee) t) t2 WHERE rn = 2 - 外层必须套一层
DISTINCT子查询,否则相同薪资会被分到不同行、不同rn - SQL Server、PostgreSQL、MySQL 8.0+、Oracle 支持;MySQL 5.7 及更早版本不支持窗口函数
- 如果只有 1 个薪资值,
rn = 2查不到结果,返回空集——和LIMIT/OFFSET行为一致
为什么不能直接用 WHERE salary = (SELECT ...)
常见错误是想当然写成 SELECT salary FROM employee WHERE salary = (SELECT DISTINCT salary FROM employee ORDER BY salary DESC LIMIT 1 OFFSET 1),这在多数数据库会报错或逻辑错误。
原因很实在:
-
WHERE ... = (subquery)要求子查询返回**恰好一行一列**,而带LIMIT/OFFSET的子查询在空结果时返回空集,不是NULL,导致语法不合法(如 MySQL 报Subquery returns more than 1 row实为误报,本质是空结果引发类型不匹配) - 即使不报错,嵌套子查询中用
LIMIT在标准 SQL 中属于非确定行为,Oracle 和 SQL Server 直接不支持 - 正确做法永远是把“找第二高”这个逻辑封装在子查询的
SELECT列中,而不是塞进WHERE条件里
真正麻烦的不是语法,而是业务定义:“第二高”到底允不允许并列?有没有人薪资为 NULL?这些细节会直接决定该用 MAX 嵌套还是窗口函数——别急着写,先跟产品对齐字段含义。

















