Supposons que nous ayons une table avec quatre colonnes (a,b,c,d)
du même type de données.
Est-il possible de sélectionner toutes les valeurs distinctes dans les données des colonnes et de les renvoyer comme une seule colonne ou dois-je créer une fonction pour y parvenir?
postgresql
postgresql-performance
postgresql-9.4
distinct
Fabrizio Mazzoni
la source
la source
SELECT a FROM tablename UNION SELECT b FROM tablename UNION SELECT c FROM tablename UNION SELECT d FROM tablename ;
?UNION
Réponses:
Mise à jour: Test des 5 requêtes dans SQLfiddle avec 100K lignes (et 2 cas séparés, un avec quelques (25) valeurs distinctes et un autre avec beaucoup (environ 25K valeurs).
Une requête très simple serait d'utiliser
UNION DISTINCT
.Je pense que ce serait plus efficace s'il y avait un index séparé sur chacune des quatre colonnes.Ce serait efficace avec un index séparé sur chacune des quatre colonnes, si Postgres avait implémenté l' optimisation Loose Index Scan , ce qu'il n'a pas. Cette requête ne sera donc pas efficace car elle nécessite 4 analyses de la table (et aucun index n'est utilisé):Une autre consisterait à utiliser d'abord
UNION ALL
et ensuiteDISTINCT
. Cela nécessitera également 4 analyses de table (et aucune utilisation d'index). Pas mal d'efficacité quand les valeurs sont peu nombreuses, et avec plus de valeurs devient la plus rapide dans mon test (pas extensif):Les autres réponses ont fourni plus d'options en utilisant les fonctions de tableau ou la
LATERAL
syntaxe. La requête de Jack (187 ms, 261 ms
) a des performances raisonnables, mais la requête d'AndriyM semble plus efficace (125 ms, 155 ms
). Les deux effectuent un balayage séquentiel de la table et n'utilisent aucun index.En fait, les résultats de la requête de Jack sont un peu meilleurs que ceux indiqués ci-dessus (si nous supprimons le
order by
) et peuvent être encore améliorés en supprimant les 4 internesdistinct
et en ne laissant que les externes.Enfin, si - et seulement si - les valeurs distinctes des 4 colonnes sont relativement peu nombreuses, vous pouvez utiliser le
WITH RECURSIVE
hack / optimisation décrit dans la page Loose Index Scan ci-dessus et utiliser les 4 index, avec un résultat remarquablement rapide! Testé avec les mêmes 100K lignes et environ 25 valeurs distinctes réparties sur les 4 colonnes (s'exécute en seulement 2 ms!) Tandis qu'avec 25K valeurs distinctes, c'est la plus lente avec 368 ms:SQLfiddle
Pour résumer, lorsque les valeurs distinctes sont peu nombreuses, la requête récursive est la gagnante absolue tandis qu'avec beaucoup de valeurs, ma deuxième, celle de Jack (version améliorée ci-dessous) et celle d'AndriyM sont les plus performantes.
Les ajouts tardifs, une variation sur la 1ère requête qui, malgré les opérations supplémentaires distinctes, fonctionne bien mieux que la 1ère originale et seulement légèrement pire que la 2ème:
et Jack amélioré:
la source
Vous pouvez utiliser LATERAL, comme dans cette requête :
Le mot clé LATERAL permet au côté droit de la jointure de référencer des objets du côté gauche. Dans ce cas, le côté droit est un constructeur VALUES qui crée un sous-ensemble à colonne unique à partir des valeurs de colonne que vous souhaitez mettre dans une seule colonne. La requête principale fait simplement référence à la nouvelle colonne, en lui appliquant également DISTINCT.
la source
Pour être clair, j'utiliserais
union
comme le suggère ypercube , mais c'est également possible avec les tableaux:dbfiddle ici
la source
Le plus court
Une version moins verbeuse de l'idée d' Andriy n'est que légèrement plus longue, mais plus élégante et plus rapide.
Pour de nombreux distinctes / quelques valeurs en double:
Le plus rapide
Avec un index sur chaque colonne impliquée!
Pour quelques valeurs distinctes / nombreuses en double:
Il s'agit d'une autre variante de rCTE, similaire à celle de @ypercube déjà publiée , mais j'utilise à la
ORDER BY 1 LIMIT 1
place,min(a)
ce qui est généralement un peu plus rapide. Je n'ai également besoin d'aucun prédicat supplémentaire pour exclure les valeurs NULL.Et
LATERAL
au lieu d'une sous-requête corrélée, car elle est plus propre (pas nécessairement plus rapide).Explication détaillée dans ma réponse à cette technique:
J'ai mis à jour SQL Fiddle de ypercube et ajouté le mien à la liste de lecture.
la source
EXPLAIN (ANALYZE, TIMING OFF)
pour vérifier les meilleures performances globales? (Le meilleur de 5 pour exclure les effets de mise en cache.)VALUES ...
est plus rapide queunnest(ARRAY[...])
.LATERAL
est implicite pour les fonctions renvoyant un ensemble dans laFROM
liste.Vous pouvez, mais pendant que j'écrivais et testais la fonction, je me sentais mal. C'est un gaspillage de ressources.
Veuillez simplement utiliser une union et plus sélectionner. Seul avantage (si c'est le cas), un seul scan depuis la table principale.
Dans sql fiddle, vous devez changer le séparateur de $ en autre chose, comme /
la source