web-dev-qa-db-fra.com

Comment sélectionner la première ligne de chaque groupe?

J'ai une table comme celle-ci:

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

Je veux faire un SELECT qui renverra juste la première ligne pour chaque Val, en ordonnant par Kind.

Exemple de sortie:

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

Comment puis-je créer cette requête?

59
BrunoLM

Cette solution utilise également keep, mais val et kind peut également être simplement calculé pour chaque groupe sans sous-requête:

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

dbfiddle ici

KEEP… FIRST et KEEP… LAST sont une fonctionnalité spécifique aux agrégats d'Oracle - vous pouvez en lire plus alors ici dans les documents Oracle, ou sur Oracle_BASE :

Les fonctions FIRST et LAST peuvent être utilisées pour renvoyer la première ou la dernière valeur d'une séquence ordonnée

39
mik

Utilisez une expression de table commune (CTE) et une fonction de fenêtrage/classement/partitionnement comme ROW_NUMBER .

Cette requête va créer une table en mémoire appelée ORDERED et ajouter une colonne supplémentaire de rn qui est une séquence de nombres de 1 à N. Le PARTITION BY indique qu'il doit redémarrer à 1 chaque fois que la valeur de Val change et nous voulons classer les lignes selon la plus petite valeur de 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'approche ci-dessus doit fonctionner avec tout SGBDR qui a implémenté la fonction ROW_NUMBER (). Oracle a des fonctionnalités élégantes exprimées en réponse de mik qui donneront généralement de meilleures performances que cette réponse.

65
billinkc

la solution de bilinkc fonctionne bien, mais je pensais que je mettrais la mienne aussi. Il a le même coût, mais pourrait être plus rapide (ou plus lent, je ne l'ai pas testé). La différence est qu'il utilise First_Value au lieu de Row_Number. Puisque nous ne nous intéressons qu'à la première valeur, à mon avis, elle est plus simple.

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;

Données de 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);

Si vous préférez, voici l'équivalent 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

Vous pouvez utiliser keep pour sélectionner un id dans chaque groupe:

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

dbfiddle ici

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

sélectionnez * à partir de (sélectionnez t1. *, ROW_NUMBER () OVER (PARTITION BY Val ORDER BY Val desc) as seqnum from tablename t1) where seqnum = 1;

0
Durgesh Yadav