Update: Alle 5 Abfragen in SQLfiddle mit 100K-Zeilen (und 2 separaten Fällen, einer mit wenigen (25) unterschiedlichen Werten und einer mit Lots (ca. 25K-Werten) getestet .
Eine sehr einfache Abfrage wäre zu verwenden UNION DISTINCT
. Ich denke, es wäre am effizientesten, wenn es einen separaten Index für jede der vier Spalten gäbe. Es wäre effizient, wenn Postgres eine Optimierung mit losem Index-Scan implementiert hätte , was nicht der Fall ist. Daher ist diese Abfrage nicht effizient, da 4 Scans der Tabelle erforderlich sind (und kein Index verwendet wird):
-- 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 ;
Eine andere wäre erstmal zu UNION ALL
benutzen und dann DISTINCT
. Dies erfordert auch 4 Tabellenscans (und keine Verwendung von Indizes). Kein schlechter Wirkungsgrad, wenn die Werte gering sind und mit mehr Werten der schnellste in meinem (nicht umfangreichen) Test wird:
-- 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 ;
Die anderen Antworten bieten mehr Optionen mit Array-Funktionen oder der LATERAL
Syntax. Jacks Abfrage ( 187 ms, 261 ms
) hat eine angemessene Leistung, aber AndriyMs Abfrage scheint effizienter ( 125 ms, 155 ms
) zu sein. Beide führen einen sequentiellen Scan der Tabelle durch und verwenden keinen Index.
Tatsächlich sind Jacks Abfrageergebnisse ein bisschen besser als oben gezeigt (wenn wir die entfernen order by
) und können weiter verbessert werden, indem die 4 internen entfernt distinct
und nur die externe gelassen werden.
Wenn - und nur wenn - die eindeutigen Werte der 4 Spalten relativ gering sind, können Sie den WITH RECURSIVE
auf der obigen Seite "Loose Index Scan" beschriebenen Hack / die Optimierung verwenden und alle 4 Indizes mit einem bemerkenswert schnellen Ergebnis verwenden! Getestet mit denselben 100K-Zeilen und ungefähr 25 unterschiedlichen Werten, die auf die 4 Spalten verteilt sind (dauert nur 2 ms!), Während es mit 25K-unterschiedlichen Werten mit 368 ms am langsamsten ist:
-- 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
Zusammenfassend ist die rekursive Abfrage der absolute Gewinner, wenn es nur wenige eindeutige Werte gibt, während die Abfragen von Jack (verbesserte Version unten) und AndriyM mit vielen Werten die besten Ergebnisse erzielen.
Späte Ergänzungen, eine Variation der ersten Abfrage, die trotz der besonders ausgeprägten Operationen viel besser als die ursprüngliche erste und nur geringfügig schlechter als die zweite ist:
-- 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 ;
und Jacks verbessert:
-- 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 ;
?