Comment sélectionner distinct pour une colonne et une autre dans une autre colonne?

29

J'ai besoin d'interroger une base de données SQL pour trouver toutes les valeurs distinctes d'une colonne et j'ai besoin d'une valeur arbitraire d'une autre colonne. Par exemple, considérez le tableau suivant avec deux colonnes, clé et valeur:

key     value
===     =====
one     test
one     another
one     value
two     goes
two     here
two     also
three   example

Je souhaite récupérer un exemple de ligne, choisi arbitrairement, à partir de chaque clé distincte, en obtenant peut-être ces trois lignes:

key     value
===     =====
one     test
two     goes
three   example

Comment puis-je formuler une telle requête en SQL?

WilliamKF
la source
2
Quel SGBD (Oracle, SQL-Server, DB2, MySQL, Postgres)?
ypercubeᵀᴹ
1
Il s'agit d'un système propriétaire.
WilliamKF

Réponses:

33

La requête la plus simple à écrire concerne MySQL (avec des paramètres ANSI non stricts). Il utilise la construction non standard:

SELECT key, value
FROM tableX
GROUP BY key ;

Dans la version récente (5.7 et 8.0+) où les paramètres stricts et ONLY_FULL_GROUP_BYpar défaut, vous pouvez utiliser la ANY_VALUE()fonction, ajoutée en 5.7:

SELECT key, ANY_VALUE(value) AS value
FROM tableX
GROUP BY key ;

Pour les autres SGBD, qui ont des fonctions de fenêtre (comme Postgres, SQL-Server, Oracle, DB2), vous pouvez les utiliser comme ceci. L'avantage est que vous pouvez également sélectionner d'autres colonnes dans le résultat (en plus de keyet value):

SELECT key, value
FROM tableX
    ( SELECT key, value,
             ROW_NUMBER() OVER (PARTITION BY key 
                                ORDER BY whatever)     --- ORDER BY NULL
               AS rn                                   --- for example
      FROM tableX
    ) tmp 
WHERE rn = 1 ;

Pour les anciennes versions de ce qui précède et pour tout autre SGBD, une manière générale qui fonctionne presque partout. Un inconvénient est que vous ne pouvez pas sélectionner d'autres colonnes avec cette approche. Un autre est que les fonctions d'agrégation aiment MIN()et MAX()ne fonctionnent pas avec certains types de données dans certains SGBD (comme bit, texte, blobs):

SELECT key, MIN(value) AS value
FROM tableX
GROUP BY key ;

PostgreSQL dispose d'un DISTINCT ONopérateur spécial non standard qui peut également être utilisé. L'option facultative ORDER BYconsiste à sélectionner la ligne de chaque groupe à sélectionner:

SELECT DISTINCT ON (key) key, value
FROM tableX
-- ORDER BY key, <some_other_expressions> ;
ypercubeᵀᴹ
la source
2
@WilliamKF Si par "choisi arbitrairement" vous voulez dire "choisi au hasard", remplacez simplement la ORDER BY whateverrequête dans ypercube par un appel à une fonction pour randomiser les résultats.
Leigh Riffel
1
@LeighRiffel Il n'a pas besoin d'être aléatoire, n'importe quel choix, aussi simple que le premier rencontré fonctionne bien.
WilliamKF
3

Pour le serveur MS-SQl:

;with FinalDataset as
(
    select *,
        row_number() over(partition by key order by value) as rownum
    from YourOriginalTable
)
select
   key,
   value
from FinalDataset 
where rownum = 1

De même, vous pourriez avoir rownum = 2 pour votre deuxième jeu de résultats

JP Chauhan
la source
2

Similaire à la réponse acceptée, mais au lieu de min () ou max (), vous pouvez utiliser array_agg ()

SELECT key, (array_agg(value))[1] AS value
FROM tableX
GROUP BY key ;

Vous pouvez éventuellement classer les valeurs dans le tableau pour en sélectionner le plus grand ou le plus petit:

SELECT key, (array_agg(value) ORDER BY value DESC)[1] AS value
FROM tableX
GROUP BY key ;

(vérifié sur PostgreSQL)

alexkovelsky
la source