Załóżmy, że mamy tabelę z czterema kolumnami (a,b,c,d)
tego samego typu danych.
Czy można wybrać wszystkie odrębne wartości w danych w kolumnach i zwrócić je jako pojedynczą kolumnę, czy muszę utworzyć funkcję, aby to osiągnąć?
postgresql
postgresql-performance
postgresql-9.4
distinct
Fabrizio Mazzoni
źródło
źródło
SELECT a FROM tablename UNION SELECT b FROM tablename UNION SELECT c FROM tablename UNION SELECT d FROM tablename ;
?UNION
Odpowiedzi:
Aktualizacja: Przetestowano wszystkie 5 zapytań w SQLfiddle ze 100 000 wierszy (i 2 oddzielnymi przypadkami, jedno z kilkoma (25) odrębnymi wartościami i drugie z partiami (około 25 000 wartości).
Można użyć bardzo prostego zapytania
UNION DISTINCT
.Myślę, że byłoby najbardziej wydajne, gdyby istniał osobny indeks w każdej z czterech kolumn.Byłoby wydajne z osobnym indeksem w każdej z czterech kolumn, gdyby Postgres zaimplementował optymalizację Loose Index Scan , czego nie ma. To zapytanie nie będzie wydajne, ponieważ wymaga 4 skanów tabeli (i nie jest używany indeks):Innym byłoby najpierw,
UNION ALL
a następnie użyćDISTINCT
. Będzie to również wymagać 4 skanów tabel (i bez użycia indeksów). Niezła wydajność, gdy wartości są nieliczne, a przy większej wartości staje się najszybsza w moim (nie obszernym) teście:Inne odpowiedzi zawierają więcej opcji przy użyciu funkcji tablicowych lub
LATERAL
składni. Zapytanie Jacka (187 ms, 261 ms
) ma rozsądną wydajność, ale zapytanie AndriyM wydaje się bardziej wydajne (125 ms, 155 ms
). Obaj wykonują jeden sekwencyjny skan tabeli i nie używają żadnego indeksu.W rzeczywistości wyniki zapytania Jacka są nieco lepsze niż pokazano powyżej (jeśli usuniemy
order by
) i można je ulepszyć, usuwając 4 wewnętrznedistinct
i pozostawiając tylko zewnętrzne.Wreszcie, jeśli - i tylko jeśli - odrębne wartości 4 kolumn są względnie nieliczne, możesz użyć
WITH RECURSIVE
hack / optymalizacji opisanej na powyższej stronie Loose Index Scan i użyć wszystkich 4 indeksów, z wyjątkowo szybkim wynikiem! Testowany z tymi samymi 100 000 wierszami i około 25 odrębnymi wartościami rozłożonymi na 4 kolumny (działa tylko 2 ms!), Natomiast z 25 000 odrębnymi wartościami jest najwolniejszy z 368 ms:SQLfiddle
Podsumowując, gdy odrębnych wartości jest niewiele, zapytanie rekurencyjne jest absolutnym zwycięzcą, podczas gdy z dużą ilością wartości, moja druga, Jack (poprawiona wersja poniżej) i zapytania AndriyM są najlepsze.
Późne dodawanie, odmiana pierwszego zapytania, które pomimo bardzo wyraźnych operacji, działa znacznie lepiej niż pierwotne pierwsze i tylko nieznacznie gorsze niż drugie:
a Jack poprawił:
źródło
Możesz użyć LATERAL, tak jak w tym zapytaniu :
Słowo kluczowe LATERAL pozwala prawej stronie złączenia odwoływać się do obiektów z lewej strony. W tym przypadku po prawej stronie znajduje się konstruktor VALUES, który buduje podzbiór jednokolumnowy z wartości kolumn, które chcesz umieścić w jednej kolumnie. Główne zapytanie po prostu odwołuje się do nowej kolumny, również stosując do niej DISTINCT.
źródło
Dla jasności użyłbym,
union
jak sugeruje ypercube , ale jest to również możliwe w przypadku tablic:dbfiddle tutaj
źródło
Najkrótszy
Mniej szczegółowa wersja pomysłu Andrija jest tylko nieco dłuższa, ale bardziej elegancka i szybsza.
W przypadku wielu różnych / kilku zduplikowanych wartości:
Najszybszy
Z indeksem w każdej zaangażowanej kolumnie!
W przypadku kilku różnych / wielu zduplikowanych wartości:
To kolejny wariant rCTE, podobny do już opublikowanego @ypercube , ale używam go
ORDER BY 1 LIMIT 1
zamiast tego,min(a)
który jest zwykle nieco szybszy. Nie potrzebuję też żadnych dodatkowych predykatów, aby wykluczyć wartości NULL.I
LATERAL
zamiast skorelowanego podkwerendy, ponieważ jest on czystszy (niekoniecznie szybszy).Szczegółowe wyjaśnienie w mojej odpowiedzi na tę technikę:
Zaktualizowałem SQL Fiddle w ypercube i dodałem mój do listy odtwarzania.
źródło
EXPLAIN (ANALYZE, TIMING OFF)
aby sprawdzić najlepszą ogólną wydajność? (Najlepsze z 5, aby wykluczyć efekty buforowania.)VALUES ...
jest szybszy niżunnest(ARRAY[...])
.LATERAL
jest niejawny dla funkcji zwracających zestaw naFROM
liście.Możesz, ale kiedy napisałem i przetestowałem funkcję, poczułem się źle. To marnotrawstwo zasobów.
Po prostu skorzystaj ze związku i wybierz więcej. Jedyna zaleta (jeśli jest), jedno skanowanie z głównej tabeli.
W sql fiddle musisz zmienić separator z $ na coś innego, takiego jak /
źródło