SQL 行值轮转,你想返回每个员工的姓名、工资,以及下一个最高和最低的工资值。如果没有找到更高或更低的工资值,你希望结果集可以“折回”(第一个 SAL
的前一行是最后一个 SAL
;反之,最后一个 SAL
的下一行即是第一个 SAL
)。
SQL 行值轮转 问题描述
你想返回每个员工的姓名、工资,以及下一个最高和最低的工资值。如果没有找到更高或更低的工资值,你希望结果集可以“折回”(第一个 SAL
的前一行是最后一个 SAL
;反之,最后一个 SAL
的下一行即是第一个 SAL
)。你希望返回如下所示的结果集。
SQL 行值轮转 解决方案
对于 Oracle 用户而言,窗口函数 LEAD OVER
和 LAG OVER
使得本问题解决起来相对容易,而且代码可读性更好。对于其他数据库,可以使用标量子查询,不过 Tie 可能会带来问题。由于存在 Tie 的问题,对于不支持窗口函数的关系数据库管理系统,我们只能提供一个近似的解决方案。
DB2、SQL Server、MySQL 和 PostgreSQL
使用标量子查询为每一个工资值找到它的下一个和前一个的工资值。
Oracle
使用窗口函数 LAG OVER
和 LEAD OVER
访问当前行的上一行和下一行记录。
SQL 行值轮转 扩展知识
DB2、SQL Server、MySQL 和 PostgreSQL
标量子查询方案并没有真正解决本问题。它只是一个近似的方案,当两行记录包含相同的 SAL
时,该解决方案就会返回不正确的结果。不过,在没有窗口函数可用的情况下,它已经是最好的方案了。
Oracle
(默认情况下,除非有特别指定。)窗口函数 LAG OVER
和 LEAD OVER
将分别返回当前行的上一行和下一行记录。“上一行”或“下一行”取决于 OVER
子句里的 ORDER BY
部分。如果仔细阅读本解决方案的代码,我们会发现它首先按照 SAL
排序数据集,并提取出了当前行的上一行和下一行。
注意,员工 SMITH 的 REWIND
是 Null
,而 KING 的 FORWARD
也是 Null
;这是因为两个人的 SAL
分别是最低值和最高值。“问题”部分提到,FORWARD
或 REWIND
若出现 Null
值,则应该“折回”。这就意味着,对于最大的 SAL
,FORWARD
值应为 EMP
表中最小的 SAL
;而对于最小的 SAL
,REWIND
值应为最大的 SAL
。没有指定分区(即 OVER
子句后面跟一对空括号)的窗口函数 MIN OVER
和 MAX OVER
将分别返回最大和最小的 SAL
。结果集如下所示。
LAG OVER
和 LEAD OVER
还有一个非常有用的功能,就是可以指定向前或者向后移动多少行。对于本例而言,我们只往前或往后移动了一行。如果你想往前移动 3 行,并且往后移动 5 行,做法非常简单。只需要指定移动值分别为 3 和 5 即可,如下所示。