Angenommen, wir haben eine Tabelle mit vier Spalten (a,b,c,d)
desselben Datentyps.
Ist es möglich, alle unterschiedlichen Werte innerhalb der Daten in den Spalten auszuwählen und als einzelne Spalte zurückzugeben, oder muss ich eine Funktion erstellen, um dies zu erreichen?
postgresql
postgresql-performance
postgresql-9.4
distinct
Fabrizio Mazzoni
quelle
quelle
SELECT a FROM tablename UNION SELECT b FROM tablename UNION SELECT c FROM tablename UNION SELECT d FROM tablename ;
?UNION
Antworten:
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 Spaltengä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):Eine andere wäre erstmal zu
UNION ALL
benutzen und dannDISTINCT
. 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: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 entferntdistinct
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: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:
und Jacks verbessert:
quelle
Sie könnten LATERAL verwenden, wie in dieser Abfrage :
Mit dem Schlüsselwort LATERAL kann die rechte Seite des Joins auf Objekte von der linken Seite verweisen. In diesem Fall ist die rechte Seite ein VALUES-Konstruktor, der aus den Spaltenwerten, die Sie in eine einzelne Spalte einfügen möchten, eine einspaltige Teilmenge erstellt. Die Hauptabfrage verweist einfach auf die neue Spalte und wendet DISTINCT darauf an.
quelle
Um es klar auszudrücken , würde ich verwenden,
union
wie es ypercube vorschlägt , aber es ist auch mit Arrays möglich:dbfiddle hier
quelle
Kürzeste
Eine weniger ausführliche Version von Andriys Idee ist nur etwas länger, aber eleganter und schneller.
Für viele eindeutige / wenige doppelte Werte:
Am schnellsten
Mit einem Index für jede beteiligte Spalte!
Für wenige eindeutige / viele doppelte Werte:
Dies ist eine andere rCTE-Variante, die der bereits veröffentlichten @ ypercube-Variante ähnelt, die ich jedoch verwende,
ORDER BY 1 LIMIT 1
stattdessenmin(a)
ist sie in der Regel etwas schneller. Ich brauche auch kein zusätzliches Prädikat, um NULL-Werte auszuschließen.Und
LATERAL
anstelle einer korrelierten Unterabfrage, weil sie sauberer ist (nicht unbedingt schneller).Detaillierte Erklärung in meiner Antwort für diese Technik:
Ich habe die SQL-Geige von ypercube aktualisiert und meiner Wiedergabeliste hinzugefügt.
quelle
EXPLAIN (ANALYZE, TIMING OFF)
, um die beste Gesamtleistung zu verifizieren? (Best of 5, um Caching-Effekte auszuschließen.)VALUES ...
ist schneller alsunnest(ARRAY[...])
.LATERAL
ist implizit für set-return-Funktionen in derFROM
Liste.Sie können, aber als ich die Funktion schrieb und testete, fühlte ich mich falsch. Es ist eine Verschwendung von Ressourcen.
Verwenden Sie einfach bitte eine Gewerkschaft und wählen Sie mehr aus. Einziger Vorteil (wenn ja), ein einziger Scan vom Haupttisch.
In SQL Fiddle müssen Sie das Trennzeichen von $ in etwas anderes ändern , wie /
quelle