it-swarm.com.de

Wie wähle ich die erste Zeile jeder Gruppe aus?

Ich habe einen Tisch wie diesen:

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

Ich möchte ein SELECT erstellen, das nur die erste Zeile für jedes Val zurückgibt, sortiert nach Kind.

Beispielausgabe:

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

Wie kann ich diese Abfrage erstellen?

59
BrunoLM

Diese Lösung verwendet auch keep, aber val und kind können auch einfach für jede Gruppe ohne Unterabfrage berechnet werden:

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

dbfiddle hier

KEEP… FIRST und KEEP… LAST sind eine Oracle-spezifische Funktion von Aggregaten - Sie können dann hier in den Oracle-Dokumenten oder auf Oracle_BASE :

Mit den Funktionen FIRST und LAST kann der erste oder letzte Wert aus einer geordneten Sequenz zurückgegeben werden

39
mik

Verwenden Sie ein allgemeiner Tabellenausdruck (CTE) und eine Fenster-/Ranking-/Partitionierungsfunktion wie ROW_NUMBER .

Diese Abfrage erstellt eine speicherinterne Tabelle mit dem Namen ORDERED und fügt eine zusätzliche Spalte von rn hinzu, die eine Folge von Zahlen von 1 bis N ist. Das PARTITION BY gibt an, dass jedes Mal, wenn der Wert von 1 neu gestartet wird Val ändert sich und wir möchten Zeilen nach dem kleinsten Wert von Kind ordnen.

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;

Der obige Ansatz sollte mit jedem RDBMS funktionieren, das die Funktion ROW_NUMBER () implementiert hat. Oracle verfügt über einige elegante Funktionen, die in Antwort von mik ausgedrückt werden und im Allgemeinen eine bessere Leistung als diese Antwort liefern.

65
billinkc

die Lösung von bilinkc funktioniert gut, aber ich dachte, ich würde meine auch rauswerfen. Es hat die gleichen Kosten, ist aber möglicherweise schneller (oder langsamer, ich habe es nicht getestet). Der Unterschied besteht darin, dass der First_Value anstelle von Row_Number verwendet wird. Da wir nur am ersten Wert interessiert sind, ist es meiner Meinung nach einfacher.

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;

Testdaten.

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

Wenn Sie es vorziehen, finden Sie hier das CTE-Äquivalent.

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;
26
Leigh Riffel

Mit keep können Sie aus jeder Gruppe ein id auswählen:

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

dbfiddle hier

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;
3
fredy

wählen Sie * aus (wählen Sie t1. *, ROW_NUMBER () OVER (PARTITION BY Val ORDER BY Val desc) als Seqnum aus Tabellenname t1), wobei Seqnum = 1;

0
Durgesh Yadav