oracle 查找每个ID的最后一行和第一行

bwitn5fc  于 2022-11-28  发布在  Oracle
关注(0)|答案(3)|浏览(339)

我有这张表:
| 标识符|等级|
| - -|- -|
| 一一一|一个|
| 一一一|2个|
| 一一一|三个|
| 二百二十二|一个|
| 二百二十二|2个|
我想添加两列,以显示这是每个ID的第一行还是最后一行
| 标识符|第一个|最后|
| - -|- -|- -|
| 一一一|是的|否|
| 一一一|否|否|
| 一一一|否|是的|
| 二百二十二|是的|否|
| 二百二十二|否|是的|

hgqdbh6s

hgqdbh6s1#

让我们首先指出,没有列的排序是没有好主意
通常,id是唯一的,并且会递增,因此按id排序就足够了。
如果不是这种情况,则至少应该有另一个具有有意义值的列(例如,也是递增数字或日期时间),可用于对结果进行排序。
因此,如果可能的话,您应该修复您的表设计,并添加这样的列或使您已经存在的id列唯一。
如果这是不可能的,并且您确实必须仅按行号排序,则可以执行以下操作:

SELECT id,
CASE WHEN rn = 1 THEN 'YES' ELSE 'NO' END AS first,
CASE WHEN rn = COUNT(*) OVER (PARTITION BY id) 
THEN 'YES' ELSE 'NO' END AS last 
FROM 
(
  SELECT
    id,
    ROW_NUMBER() OVER (PARTITION BY id ORDER BY id) rn
  FROM yourtable
);

如果你有一个列要排序(让我们把它命名为“rank”),这样会安全得多:

SELECT id,
CASE WHEN rn1 = 1 THEN 'YES' ELSE 'NO' END AS first,
CASE WHEN rn2 = 1 THEN 'YES' ELSE 'NO' END AS last
FROM 
(
  SELECT
   id,
   ROW_NUMBER() OVER (PARTITION BY id ORDER BY rank) rn1,
   ROW_NUMBER() OVER (PARTITION BY id ORDER BY rank DESC) rn2
  FROM yourtable
);
8fq7wneg

8fq7wneg2#

这里有一个选项:
示例数据:

SQL> with
  2  test (id, rank) as
  3    (select 111, 1 from dual union all
  4     select 111, 2 from dual union all
  5     select 111, 3 from dual union all
  6     select 222, 1 from dual union all
  7     select 222, 2 from dual
  8    ),

查询从此处开始:

9  temp as
 10    (select id,
 11       rank,
 12       first_value(rank) over (partition by id) rnk_min,
 13       last_value(rank)  over (partition by id ) rnk_max
 14     from test
 15    )
 16  select id,
 17    case when rank = rnk_min then 'Yes' else 'No' end first,
 18    case when rank = rnk_max then 'Yes' else 'No' end last
 19  from temp
 20  order by id, rank;

        ID FIRST   LAST
---------- ------- -------
       111 Yes     No
       111 No      No
       111 No      Yes
       222 Yes     No
       222 No      Yes

SQL>
cygmwpex

cygmwpex3#

If you don't have rows with the same rank per id, you may use lag/lead functions to mark first and last rows with a flag using default argument of these functions, which is used when the function leaves a window boundary.

with sample_tab (id, rank) as (
  select 111, 1 from dual union all
  select 111, 2 from dual union all
  select 111, 3 from dual union all
  select 222, 1 from dual union all
  select 222, 2 from dual
)
select
  id
  , lag('No', 1, 'Yes') over(partition by id order by rank asc) as last
  , lead('No', 1, 'Yes') over(partition by id order by rank asc) as last
from sample_tab
IDLASTLAST
111YesNo
111NoNo
111NoYes
222YesNo
222NoYes

If the data may have the same rank for multiple rows per id, you may use the same technique (a case when function goes beyound window boundary) with coalesce .

with sample_tab (id, rank) as (
  select 111, 1 from dual union all
  select 111, 2 from dual union all
  select 111, 2 from dual union all
  select 222, 1 from dual union all
  select 222, 2 from dual
)
select
  id
  , coalesce(max('No') over(
      partition by id order by rank asc
      /*RANGE for logical offset,
      setting the same flag for a group of first/last rows*/
      range between 1 preceding and 1 preceding
  ), 'Yes') as first
  , coalesce(max('No') over(
      partition by id order by rank asc
      range between 1 following and 1 following
  ), 'Yes') as last
from sample_tab
IDFIRSTLAST
111YesNo
111NoYes
111NoYes
222YesNo
222NoYes

fiddle

相关问题