Musiałem napisać proste zapytanie, w którym szukam nazwiska ludzi, które zaczynają się na B lub D:
SELECT s.name
FROM spelers s
WHERE s.name LIKE 'B%' OR s.name LIKE 'D%'
ORDER BY 1
Zastanawiałem się, czy istnieje sposób na przepisanie tego, aby stać się bardziej wydajnym. Więc mogę uniknąć or
i / lub like
?
postgresql
performance
index
regular-expression
pattern-matching
Lucas Kauffman
źródło
źródło
s.name
indeksowany?name
może być przydatny tutaj, jeśli zależy Ci na wydajności.Odpowiedzi:
Twoje zapytanie jest w zasadzie optymalne. Składnia nie będzie znacznie krótsza, zapytanie nie będzie znacznie szybsze:
Jeśli naprawdę chcesz skrócić składnię , użyj wyrażenia regularnego z rozgałęzieniami :
Lub nieco szybciej, z klasą postaci :
Szybki test bez indeksu daje szybsze wyniki niż
SIMILAR TO
w obu przypadkach dla mnie.Przy odpowiednim indeksie B-Tree
LIKE
wygrywa ten wyścig o rzędy wielkości.Przeczytaj podstawowe informacje na temat dopasowywania wzorów w instrukcji .
Indeks doskonałej wydajności
Jeśli martwisz się wydajnością, utwórz taki indeks dla większych tabel:
Przyspiesza tego rodzaju zapytania o rzędy wielkości. Szczególne uwagi dotyczą kolejności sortowania specyficznej dla danego regionu. Przeczytaj więcej o klasach operatorów w instrukcji . Jeśli używasz standardowych ustawień regionalnych „C” (większość ludzi tego nie robi), zrobi to zwykły indeks (z domyślną klasą operatora).
Taki indeks jest dobry tylko dla wzorców zakotwiczonych w lewo (dopasowanie od początku łańcucha).
SIMILAR TO
lub wyrażenia regularne z podstawowymi wyrażeniami zakotwiczonymi w lewo również mogą korzystać z tego indeksu. Ale nie z gałęziami(B|D)
lub klasami znaków[BD]
(przynajmniej w moich testach na PostgreSQL 9.0).Dopasowania Trigram lub wyszukiwanie tekstu używają specjalnych indeksów GIN lub GiST.
Przegląd operatorów dopasowywania wzorców
LIKE
(~~
) jest prosty i szybki, ale ma ograniczone możliwości.ILIKE
(~~*
) wariant bez rozróżniania wielkości liter.pg_trgm rozszerza obsługę indeksu dla obu.
~
(dopasowanie wyrażeń regularnych) jest potężne, ale bardziej złożone i może być powolne w przypadku czegoś więcej niż wyrażeń podstawowych.SIMILAR TO
jest po prostu bezcelowe . Osobliwa półrasaLIKE
i wyrażenia regularne. Nigdy tego nie używam. Patrz poniżej.% jest operatorem „podobieństwa” zapewnianym przez dodatkowy moduł
pg_trgm
. Patrz poniżej.@@
jest operatorem wyszukiwania tekstu. Patrz poniżej.pg_trgm - dopasowanie trygramu
Począwszy od PostgreSQL 9.1 możesz ułatwić rozszerzenie,
pg_trgm
aby zapewnić obsługę indeksu dla dowolnego wzorcaLIKE
/ILIKE
(i prostych wzorców~
wyrażeń regularnych z ) za pomocą indeksu GIN lub GiST.Szczegóły, przykład i linki:
pg_trgm
zapewnia również tych operatorów :%
- operator „podobieństwa”<%
(komutator%>
:) - operator „word_similarity” w Postgresie 9.6 lub nowszym<<%
(komutator%>>
:) - operator „strict_word_similarity” w Postgres 11 lub nowszyWyszukiwanie tekstu
Jest specjalnym rodzajem dopasowania wzorca z osobnymi typami infrastruktury i indeksu. Korzysta ze słowników i wyszukiwania oraz jest doskonałym narzędziem do wyszukiwania słów w dokumentach, szczególnie w przypadku języków naturalnych.
Obsługiwane jest również dopasowanie prefiksu :
Jak również wyszukiwanie fraz od Postgres 9.6:
Rozważ wprowadzenie w podręczniku oraz przegląd operatorów i funkcji .
Dodatkowe narzędzia do dopasowywania rozmytych ciągów znaków
Dodatkowy moduł fuzzystrmatch oferuje kilka dodatkowych opcji, ale wydajność jest ogólnie gorsza od wszystkich powyższych.
W szczególności różne implementacje
levenshtein()
funkcji mogą być instrumentalne.Dlaczego wyrażenia regularne (
~
) są zawsze szybsze niżSIMILAR TO
?Odpowiedź jest prosta.
SIMILAR TO
wyrażenia są wewnętrznie przepisywane na wyrażenia regularne. Tak więc dla każdegoSIMILAR TO
wyrażenia istnieje co najmniej jedno szybsze wyrażenie regularne (co pozwala zaoszczędzić koszty przepisywania wyrażenia).SIMILAR TO
Nigdy nie zyskujesz na wydajności .A proste wyrażenia, które można wykonać za pomocą
LIKE
(~~
), są iLIKE
tak szybsze .SIMILAR TO
jest obsługiwany tylko w PostgreSQL, ponieważ skończył we wczesnych wersjach językowych standardu SQL. Nadal się tego nie pozbyli. Ale są plany, aby go usunąć i dołączyć dopasowania wyrażeń regularnych - a przynajmniej tak słyszałem.EXPLAIN ANALYZE
ujawnia to. Po prostu spróbuj sam z dowolnym stołem!Ujawnia:
SIMILAR TO
został przepisany wyrażeniem regularnym (~
).Najwyższa wydajność w tym konkretnym przypadku
Ale
EXPLAIN ANALYZE
ujawnia więcej. Spróbuj, korzystając z wyżej wymienionego indeksu:Ujawnia:
Wewnętrznie, z indeksem, który nie jest świadomy Locale (
text_pattern_ops
lub przy użyciu localeC
) proste wyrażenia lewe zakotwiczone są przepisywane z tych operatorów wzór tekst:~>=~
,~<=~
,~>~
,~<~
. Tak jest w przypadku~
,~~
lubSIMILAR TO
podobnie.To samo dotyczy indeksów
varchar
typów zvarchar_pattern_ops
lubchar
zbpchar_pattern_ops
.Tak więc, zastosowany do pierwotnego pytania, jest to najszybszy możliwy sposób :
Oczywiście, jeśli zdarzy ci się szukać sąsiednich inicjałów , możesz uprościć dalej:
Zysk w porównaniu do zwykłego użycia
~
lub~~
jest niewielki. Jeśli wydajność nie jest twoim najważniejszym wymogiem, powinieneś po prostu trzymać się standardowych operatorów - osiągając to, co już masz w pytaniu.źródło
similar
skan?EXPLAIN ANALYZE
2 skanami indeksu bitmap. Wiele skanów indeksów bitmapowych można łączyć dość szybko.OR
zUNION ALL
lub zastępującname LIKE 'B%'
zename >= 'B' AND name <'C'
w PostgreSQL?UNION
nie, ale tak, połączenie zakresów w jednąWHERE
klauzulę przyspieszy zapytanie. Dodałem więcej do mojej odpowiedzi. Oczywiście musisz wziąć pod uwagę swoje ustawienia regionalne. Wyszukiwanie uwzględniające ustawienia regionalne jest zawsze wolniejsze.Co powiesz na dodanie kolumny do tabeli. W zależności od aktualnych wymagań:
PostgreSQL nie obsługuje kolumn obliczanych w tabelach podstawowych a SQL Server, ale nową kolumnę można obsługiwać za pomocą wyzwalacza. Oczywiście ta nowa kolumna zostałaby zaindeksowana.
Alternatywnie, indeks wyrażenia dałby ci to samo, tańsze. Na przykład:
Zapytania pasujące do wyrażenia w ich warunkach mogą korzystać z tego indeksu.
W ten sposób uderzenie wydajności jest pobierane, gdy dane są tworzone lub zmieniane, więc może być odpowiednie tylko w środowisku o niskiej aktywności (tj. Znacznie mniej zapisów niż odczytów).
źródło
Możesz spróbować
Nie mam pojęcia, czy powyższe, czy też twoje oryginalne wypowiedzi są dostępne w Postgres.
Jeśli utworzysz sugerowany indeks, zainteresuje Cię również porównanie tego z innymi opcjami.
źródło
W przeszłości, w obliczu podobnego problemu z wydajnością, zwiększałem znak ASCII ostatniej litery i robiłem MIĘDZY. Otrzymujesz wtedy najlepszą wydajność, dla podzbioru funkcjonalności LIKE. Oczywiście działa to tylko w niektórych sytuacjach, ale w przypadku bardzo dużych zestawów danych, w których np. Szukasz nazwy, wydajność spada z fatalnej do akceptowalnej.
źródło
Bardzo stare pytanie, ale znalazłem inne szybkie rozwiązanie tego problemu:
Ponieważ funkcja ascii () patrzy tylko na pierwszy znak ciągu.
źródło
(name)
?Do sprawdzania inicjałów często używam rzutowania na
"char"
(z podwójnymi cudzysłowami). Nie jest przenośny, ale bardzo szybki. Wewnętrznie po prostu usuwa tekst i zwraca pierwszy znak, a operacje porównywania „char” są bardzo szybkie, ponieważ typ ma stałą długość 1-bajta:Zauważ, że rzutowanie na
"char"
jest szybsze niżascii()
odchylenie przez @ Sole021, ale nie jest kompatybilne z UTF8 (ani żadnym innym kodowaniem w tym zakresie), zwracając po prostu pierwszy bajt, więc powinno się go używać tylko w przypadkach, gdy porównanie jest przeciwko zwykłemu staremu 7 -bitowe znaki ASCII.źródło
Istnieją dwie niewymienione jeszcze metody postępowania w takich przypadkach:
indeks częściowy (lub podzielony na partycje - jeśli utworzono go ręcznie dla pełnego zakresu) - najbardziej przydatny, gdy wymagany jest tylko podzbiór danych (na przykład podczas niektórych czynności konserwacyjnych lub tymczasowy w przypadku niektórych raportów):
partycjonowanie samej tabeli (użycie pierwszego znaku jako klucza partycjonowania) - ta technika jest szczególnie warta rozważenia w PostgreSQL 10+ (mniej bolesne partycjonowanie) i 11+ (czyszczenie partycji podczas wykonywania zapytania).
Ponadto, jeśli dane w tabeli zostaną posortowane, można skorzystać z indeksu BRIN (nad pierwszym znakiem).
źródło
Prawdopodobnie szybciej wykonać porównanie jednego znaku:
źródło
column LIKE 'B%'
będzie bardziej wydajny niż użycie funkcji podłańcuchowej w kolumnie.