各グループの最初の行を選択する方法は?


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

このクエリを作成するにはどうすればよいですか?


4 | 3 | 4ではなく3 | 3 | 4である理由-タイブレークとは何ですか?
ジャックダグラス

@JackDouglas実際、私はを持ってORDER BY ID DESCいますが、それは質問には関係ありません。この例では気にしません。
ブルーノLM

回答:


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

LAST集計のOracle固有の機能です... ... KEEP FIRSTとKEEP -あなたはについて、次に読むことができ、ここでのOracleドキュメントで、またはORACLE_BASEに

FIRSTおよびLAST関数を使用して、順序付けられたシーケンスから最初または最後の値を返すことができます


62

使用する共通テーブル式(CTE)などのウィンドウ/ランキング/パーティショニング機能ROW_NUMBERを

このクエリは、メモリ内のテーブルを注文呼ば作成し、1からNまでの数字の並びであるRNの追加の列を追加します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で動作するはずです。Oracleにはmikの回答で表されているように、一般的にこの回答よりも優れたパフォーマンスが得られるエレガントな機能があります。


25

bilinkcのソリューションは正常に機能しますが、私も同様に私のものを放り投げると思いました。コストは同じですが、もっと速いかもしれません(または、もっと遅いかもしれませんが、テストしていません)。違いは、Row_Numberの代わりにFirst_Valueを使用することです。最初の値にのみ関心があるので、私の考えではもっと簡単です。

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ですが、あなたの答えとbillinkc idは一意でない限り論理的に同じではないことを強調する価値があると思いました。
ジャックダグラス

@Jack Douglas-本当だと思いました。
リーリッフェル

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を介した2つのスキャンが必要であるという事実により、他の回答よりもはるかに効率が悪くなります。
a_horse_with_no_name

2
これは、オプティマイザーが書き込まれたクエリを文字通りに受け取る場合にのみ当てはまります。より高度なオプティマイザーは、インテント(グループごとの行)を確認し、単一のテーブルアクセスでプランを作成できます。
ポールホワイト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.