it-swarm.it

Come selezionare la prima riga di ciascun gruppo?

Ho un tavolo come questo:

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

Voglio creare un SELECT che restituirà solo la prima riga per ogni Val, ordinando per Kind.

Uscita campione:

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

Come posso creare questa query?

59
BrunoLM

Questa soluzione utilizza anche keep, ma val e kind possono anche essere semplicemente calcolati per ciascun gruppo senza una sottoquery:

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

dbfiddle qui

MANTENERE ... PRIMO e MANTENERE ... ULTIMO sono una funzione specifica di Oracle degli aggregati - puoi leggere allora qui nei documenti Oracle, oppure su Oracle_BASE :

Le funzioni FIRST e LAST possono essere utilizzate per restituire il primo o l'ultimo valore da una sequenza ordinata

39
mik

Utilizzare una espressione di tabella comune (CTE) e una funzione di finestra/classifica/partizionamento come ROW_NUMBER .

Questa query creerà una tabella in memoria chiamata ORDERED e aggiungerà una colonna aggiuntiva di rn che è una sequenza di numeri da 1 a N. Il PARTITION BY indica che dovrebbe riavviare a 1 ogni volta che il valore di Val cambia e vogliamo ordinare le righe in base al valore più piccolo di 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;

L'approccio sopra dovrebbe funzionare con qualsiasi RDBMS che ha implementato la funzione ROW_NUMBER (). Oracle ha alcune eleganti funzionalità come espresso in risposta di Mik che generalmente produrrà prestazioni migliori di questa risposta.

65
billinkc

la soluzione di bilinkc funziona bene, ma ho pensato di buttare via anche la mia. Ha lo stesso costo, ma potrebbe essere più veloce (o più lento, non l'ho provato). La differenza è che utilizza First_Value anziché Row_Number. Dal momento che siamo interessati solo al primo valore, nella mia mente è più semplice.

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;

Dati di test.

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

Se preferisci, ecco l'equivalente 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

Puoi usare keep per selezionare un id da ciascun gruppo:

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

dbfiddle qui

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

selezionare * da (selezionare t1. *, ROW_NUMBER () OVER (PARTITION BY Val ORDER BY Val desc) come seqnum dal tablename t1) dove seqnum = 1;

0
Durgesh Yadav