如何选择每个组的第一行?


57

我有一张这样的桌子:

 ID |  Val   |  Kind
----------------------
 1  |  1337  |   2
 2  |  1337  |   1
 3  |   3    |   4
 4  |   3    |   4

我想制作一个SELECT将仅返回第一行的Val命令,按排序Kind

样本输出:

 ID |  Val   |  Kind
----------------------
 2  |  1337  |   1
 3  |   3    |   4

如何建立这个查询?


为什么3 | 3 | 4而不是4 | 3 | 4-抢七是什么?还是您不在乎?
杰克·道格拉斯

@JackDouglas实际上我有一个ORDER BY ID DESC,但这与问题无关。在这个例子中我不在乎。
BrunoLM 2011年

Answers:


38

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

select min(id) keep(dense_rank first order by kind) id
     , val
     , min(kind) kind
  from mytable
 group by val;
ID | VAL | 类
-:| ---:---:
 3 | 3 | 4
 2 | 1337 | 1个

dbfiddle 在这里

KEEP ...首先,保持...最后是聚集的Oracle特定的功能-你可以读到那么这里在Oracle文档,或在ORACLE_BASE

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


62

使用公用表表达式(CTE)和诸如ROW_NUMBER的窗口/排序/分区功能。

这个查询将创建一个名为下令在内存中的表,并添加RN的附加列是数字从1序列N的PARTITION BY表明它应该在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;

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


25

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;

测试数据。

--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);

如果您愿意,这里是等效的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;

1
+1,但我只是想强调一下,除非逻辑上id唯一,否则您的答案和billinkc的逻辑上并不相同。
杰克·道格拉斯

@杰克·道格拉斯-是的,我以为是。
Leigh Riffel

14

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

select *
from mytable
where id in ( select min(id) keep (dense_rank first order by kind, id)
              from mytable
              group by val );
ID | VAL | 类
-:| ---:---:
 2 | 1337 | 1个
 3 | 3 | 4

dbfiddle 在这里


2
SELECT MIN(MyTable01.Id) as Id,
       MyTable01.Val     as Val,
       MyTable01.Kind    as Kind 
  FROM MyTable MyTable01,                         
       (SELECT Val,MIN(Kind) as Kind
          FROM MyTable                   
      GROUP BY Val) MyTableGroup
WHERE MyTable01.Val  = MyTableGroup.Val
  AND MyTable01.Kind = MyTableGroup.Kind
GROUP BY MyTable01.Val,MyTable01.Kind
ORDER BY Id;

由于需要对MyTable进行两次扫描,因此这将比其他答案效率低很多。
a_horse_with_no_name

2
仅当优化程序从字面上接受书面查询时,这才是正确的。更高级的优化器可以查看意图(每组行),并通过单个表访问来制定计划。
保罗·怀特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.