it-swarm.dev

Jak wybrać pierwszy rząd każdej grupy?

Mam taki stół:

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

Chcę utworzyć SELECT, która zwróci tylko pierwszy wiersz dla każdego Val, sortując według Kind.

Przykładowe dane wyjściowe:

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

Jak mogę zbudować to zapytanie?

59
BrunoLM

To rozwiązanie używa również keep, ale val i kind można również po prostu obliczyć dla każdej grupy bez podzapytania:

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

dbfiddle tutaj

KEEP… FIRST i KEEP… LAST to specyficzna dla Oracle funkcja agregatów - możesz przeczytać o tutaj w dokumentach Oracle, lub na Oracle_BASE :

Funkcje PIERWSZA i OSTATNIA mogą być użyte do zwrócenia pierwszej lub ostatniej wartości z uporządkowanej sekwencji

39
mik

Użyj wspólne wyrażenie tablicowe (CTE) i funkcji okienkowania/rankingu/partycjonowania, takich jak ROW_NUMBER .

Ta kwerenda utworzy tabelę w pamięci o nazwie ORDERED i doda dodatkową kolumnę rn, która jest sekwencją liczb od 1 do N. PARTITION BY oznacza, że ​​należy ją zrestartować o 1 za każdym razem, gdy wartość Wartość zmienia się i chcemy uporządkować wiersze według najmniejszej wartości Rodzaj.

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;

Powyższe podejście powinno działać z każdym RDBMS, który zaimplementował funkcję ROW_NUMBER (). Oracle ma pewną elegancką funkcjonalność, wyrażoną w odpowiedź mik , która ogólnie przyniesie lepszą wydajność niż ta odpowiedź.

65
billinkc

rozwiązanie bilinkca działa dobrze, ale pomyślałem, że też go wyrzucę. Ma taki sam koszt, ale może być szybszy (lub wolniejszy, nie testowałem go). Różnica polega na tym, że używa First_Value zamiast Row_Number. Ponieważ interesuje nas tylko pierwsza wartość, moim zdaniem jest to prostsze.

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;

Dane testowe.

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

Jeśli wolisz, oto odpowiednik 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;
26
Leigh Riffel

Możesz użyć keep, aby wybrać id z każdej grupy:

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

dbfiddle tutaj

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

wybierz * z (wybierz t1. *, ROW_NUMBER () OVER (PARTITION BY Val ORDER BY Val desc) jako seqnum z tablename t1) gdzie seqnum = 1;

0
Durgesh Yadav