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é):
-- Query 1. (334 ms, 368ms)
SELECT a AS abcd FROM tablename
UNION -- means UNION DISTINCT
SELECT b FROM tablename
UNION
SELECT c FROM tablename
UNION
SELECT d FROM tablename ;
Une autre consisterait à utiliser d'abord UNION ALL
et ensuite DISTINCT
. 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):
-- Query 2. (87 ms, 117 ms)
SELECT DISTINCT a AS abcd
FROM
( SELECT a FROM tablename
UNION ALL
SELECT b FROM tablename
UNION ALL
SELECT c FROM tablename
UNION ALL
SELECT d FROM tablename
) AS x ;
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 internes distinct
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:
-- Query 3. (2 ms, 368ms)
WITH RECURSIVE
da AS (
SELECT min(a) AS n FROM observations
UNION ALL
SELECT (SELECT min(a) FROM observations
WHERE a > s.n)
FROM da AS s WHERE s.n IS NOT NULL ),
db AS (
SELECT min(b) AS n FROM observations
UNION ALL
SELECT (SELECT min(b) FROM observations
WHERE b > s.n)
FROM db AS s WHERE s.n IS NOT NULL ),
dc AS (
SELECT min(c) AS n FROM observations
UNION ALL
SELECT (SELECT min(c) FROM observations
WHERE c > s.n)
FROM dc AS s WHERE s.n IS NOT NULL ),
dd AS (
SELECT min(d) AS n FROM observations
UNION ALL
SELECT (SELECT min(d) FROM observations
WHERE d > s.n)
FROM db AS s WHERE s.n IS NOT NULL )
SELECT n
FROM
( TABLE da UNION
TABLE db UNION
TABLE dc UNION
TABLE dd
) AS x
WHERE n IS NOT NULL ;
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:
-- Query 1b. (85 ms, 149 ms)
SELECT DISTINCT a AS n FROM observations
UNION
SELECT DISTINCT b FROM observations
UNION
SELECT DISTINCT c FROM observations
UNION
SELECT DISTINCT d FROM observations ;
et Jack amélioré:
-- Query 4b. (104 ms, 128 ms)
select distinct unnest( array_agg(a)||
array_agg(b)||
array_agg(c)||
array_agg(d) )
from t ;
SELECT a FROM tablename UNION SELECT b FROM tablename UNION SELECT c FROM tablename UNION SELECT d FROM tablename ;
?