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?
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
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.
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;
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;
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;