如何选择每组的第一行?

Bru*_*oLM 67 oracle greatest-n-per-group

我有一张这样的表:

 ID |  Val   |  Kind
----------------------
 1  |  1337  |   2
 2  |  1337  |   1
 3  |   3    |   4
 4  |   3    |   4
Run Code Online (Sandbox Code Playgroud)

我想制作一个SELECT只返回每个 的第一行,按Val排序Kind

示例输出:

 ID |  Val   |  Kind
----------------------
 2  |  1337  |   1
 3  |   3    |   4
Run Code Online (Sandbox Code Playgroud)

如何构建此查询?

bil*_*nkc 73

使用公共表表达式(CTE) 和窗口/排名/分区函数,如ROW_NUMBER

这个查询将创建一个名为 ORDERED 的内存表,并添加一个额外的 rn 列,它是一个从 1 到 N 的数字序列。PARTITION BY表示每次 Val 的值改变时它应该从 1重新开始,我们想要排序按 Kind 的最小值排列。

WITH ORDERED AS
(
SELECT
    ID
,   Val
,   kind
,   ROW_NUMBER() OVER (PARTITION BY Val ORDER BY Kind ASC) AS rn
FROM
    mytable
)
SELECT
    ID
,   Val
,   Kind
FROM
    ORDERED
WHERE
    rn = 1;
Run Code Online (Sandbox Code Playgroud)

上述方法应该适用于任何已实现 ROW_NUMBER() 函数的 RDBMS。如mik 的回答所示,Oracle 具有一些优雅的功能,通常会产生比此答案更好的性能。


小智 46

该解决方案还使用了keep,但valkind也可以简单地计算出每个组没有子查询:

select min(id) keep(dense_rank first order by kind) id
     , val
     , min(kind) kind
  from mytable
 group by val;
Run Code Online (Sandbox Code Playgroud)
身份证 | 价值 | 种类
-: | ---: | ---:
 3 | 3 | 4
 2 | 第1337章 1

dbfiddle在这里

KEEP...FIRST 和 KEEP...LAST 是特定于 Oracle 的聚合特性——您可以在此处的 Oracle 文档或ORACLE_BASE 上阅读

FIRST 和 LAST 函数可用于返回有序序列的第一个或最后一个值


Lei*_*fel 27

bilinkc 的解决方案工作正常,但我想我也会扔掉我的。它具有相同的成本,但可能更快(或更慢,我还没有测试过)。不同之处在于它使用 First_Value 而不是 Row_Number。因为我们只对第一个值感兴趣,所以在我看来它更直接。

SELECT ID, Val, Kind FROM
(
   SELECT First_Value(ID) OVER (PARTITION BY Val ORDER BY Kind) First, ID, Val, Kind 
   FROM mytable
)
WHERE ID = First;
Run Code Online (Sandbox Code Playgroud)

测试数据。

--drop table mytable;
create table mytable (ID Number(5) Primary Key, Val Number(5), Kind Number(5));

insert into mytable values (1,1337,2);
insert into mytable values (2,1337,1);
insert into mytable values (3,3,4);
insert into mytable values (4,3,4);
Run Code Online (Sandbox Code Playgroud)

如果您愿意,这里是 CTE 等效项。

WITH FirstIDentified AS (
   SELECT First_Value(ID) OVER (PARTITION BY Val ORDER BY Kind) First, ID, Val, Kind 
   FROM mytable
   )
SELECT ID, Val, Kind FROM FirstIdentified
WHERE ID = First;
Run Code Online (Sandbox Code Playgroud)


Jac*_*las 17

您可以使用从每个组中keep选择一个id

select *
from mytable
where id in ( select min(id) keep (dense_rank first order by kind, id)
              from mytable
              group by val );
Run Code Online (Sandbox Code Playgroud)
身份证 | 价值 | 种类
-: | ---: | ---:
 2 | 第1337章 1
 3 | 3 | 4

dbfiddle在这里