使用单个选择语句从表中获取下一行,或者如果到达表的末尾则返回第一行

时间:2016-09-01 03:58:12

标签: oracle plsql

我有一张表说STAFF如下:

STAFF_NAME
============
ALEX
BERNARD
CARL
DOMINIC
EMMA

现在,我想用单个参数编写一个存储函数。例如。 GET_NEXT_STAFF(CURRENT_STAFF).

输入和输出应该是:

Input   |   Output
=====================
NULL    | ALEX
ALEX    | BERNARD
BERNARD | CARL
EMMA    | ALEX (Start from the beginning of the table again)

我知道如何使用PL / SQL处理这个问题,但是用一个select语句可以处理这个问题吗?

2 个答案:

答案 0 :(得分:2)

在下面的解决方案中,我假设行按名称按字母顺序排序。它们可能由同一个表中的另一列(例如按雇用日期,或通过工资等等 - 按顺序排序) - 然后该列的名称应该在两个分析的ORDER BY子句中使用功能

输入名称作为绑定变量:input_staff_name传入。该解决方案使用纯SQL,不需要函数(PL / SQL),但如果必须将其转换为函数,则可以轻松地对其进行调整。

修改:在我的原始回答中,当输入为null时,我错过了所需的行为。最后一行代码(不包括分号)负责处理。如当前所写,当输入为null时,查询返回ALEX(或通常是表中的第一个值),并且当输入不为空且不在表中时,它不返回任何行。如果要求是在输入为null或在表格中找不到时返回名字,则可以通过从最后一行删除and :input_staff_name is null来轻松容纳它。

with
     tbl ( staff_name ) as (
       select 'ALEX'    from dual union all
       select 'BERNARD' from dual union all
       select 'CARL'    from dual union all
       select 'DOMINIC' from dual union all
       select 'EMMA'    from dual
     ),
     prep ( staff_name, next_name, first_name ) as (
       select staff_name, 
              lead(staff_name)         over (order by staff_name),
              first_value (staff_name) over (order by staff_name)
       from   tbl
     )
select nvl(next_name, first_name) as next_staff_name
from   prep
where  staff_name = :input_staff_name 
   or  (next_name is null and :input_staff_name is null)
;

答案 1 :(得分:0)

根据@mathguy的回答,我做了一些似乎有效的改动。我添加了以下

UNION ALL
 SELECT NULL
   FROM DUAL

WHERE NVL (staff_name, 'X') = NVL (NULL, 'X');

完整代码

WITH tbl (staff_name) AS
    (SELECT 'ALEX' FROM DUAL
     UNION ALL
     SELECT 'BERNARD' FROM DUAL
     UNION ALL
     SELECT 'CARL' FROM DUAL
     UNION ALL
     SELECT 'DOMINIC' FROM DUAL
     UNION ALL
     SELECT 'EMMA' FROM DUAL
     UNION ALL
     SELECT NULL
       FROM DUAL),
 prep (staff_name,
       next_name,
       first_name,
       last_name) AS
    (SELECT staff_name,
            LEAD (staff_name) OVER (ORDER BY staff_name),
            FIRST_VALUE (staff_name) OVER (ORDER BY staff_name),
            LAG (staff_name) OVER (ORDER BY staff_name)
       FROM tbl) 
SELECT NVL (next_name, first_name) AS next_staff_name
  FROM prep
 WHERE NVL (staff_name, 'X') = NVL (:input_staff_name, 'X');