Czy PostgreSQL obsługuje kolacje "niewrażliwe na akcent"?

W Microsoft SQL Server można określić kolacje " niewrażliwe na akcent "(dla bazy danych, tabeli lub kolumny), co oznacza, że jest to możliwe dla zapytania typu

SELECT * FROM users WHERE name LIKE 'João'

Aby znaleźć wiersz o nazwie Joao.

Wiem, że możliwe jest usunięcie akcentów z napisów w PostgreSQL przy użyciu funkcji contrib unaccent_string , ale zastanawiam się, czy PostgreSQL obsługuje te "nieczułe" zestawienia, więc SELECT powyżej by zadziałało.

Author: Erwin Brandstetter, 2012-06-13

3 answers

Użyj moduł unaccent do tego-co jest zupełnie inne od tego, do czego linkujesz.

Unaccent jest słownikiem wyszukiwania tekstu, który usuwa akcenty (znaki diakrytyczne znaki) z leksemes.

Zainstaluj raz na bazę danych za pomocą:

CREATE EXTENSION unaccent;

Jeśli pojawi się błąd w stylu:

Błąd: nie można otworzyć pliku kontroli rozszerzenia "/usr / share / postgresql / 9.x / extension / unaccent.control": brak takiego pliku lub katalog

Zainstaluj pakiet contrib na serwerze bazy danych zgodnie z Instrukcją w tej powiązanej odpowiedzi:

Zawiera między innymi funkcję unaccent(), której możesz użyć w swoim przykładzie (gdzie LIKE wydaje się nie być potrzebna).

SELECT *
FROM   users
WHERE  unaccent(name) = unaccent('João');

Indeks

Aby użyć indeksu dla tego rodzaju zapytań, Utwórz indeks na wyrażeniu. Jednakże , Postgres akceptuje tylko funkcje IMMUTABLE dla indeksów. Jeśli funkcja może zwrócić inny wynik dla tego samego wejścia, indeks może się po cichu złamać.

unaccent() tylko STABLE Nie IMMUTABLE

Niestety, unaccent() jest tylko STABLE, a nie IMMUTABLE. Zgodnie z ten wątek na pgsql-bugs, wynika to z trzech powodów:

    To zależy od zachowania słownika.
  1. nie ma połączenia przewodowego z tym słownikiem.
  2. dlatego też zależy od prądu search_path, który może się łatwo zmienić.

Niektóre tutoriale w sieci instruują, aby po prostu zmienić zmienność funkcji na IMMUTABLE. Ta metoda brute-force może pęknąć w pewnych warunkach.

Inni sugerują prostą IMMUTABLE funkcję wrappera (tak jak ja sam w przeszłości).

Trwa dyskusja czy zrobić wariant z dwoma parametrami IMMUTABLE który wyraźnie deklaruje używany słownik. Czytaj tutaj lub tutaj .

Inną alternatywą byłby ten moduł z niezmienną unaccent() funkcją Musicbrainz , dostarczoną na Githubie. Sam tego nie testowałem. Myślę, że wymyśliłem lepszy pomysł:

Najlepsze na razie

Proponuję podejście, które jest co najmniej równie wydajne jak inne rozwiązania, ale bezpieczniejsze.]}: Utwórz funkcję owijania z dwu-parametrowym formularzem i "twardym drutem" schemat funkcji i słownika:
CREATE OR REPLACE FUNCTION public.f_unaccent(text)
  RETURNS text AS
$func$
SELECT public.unaccent('public.unaccent', $1)  -- schema-qualify function and dictionary
$func$  LANGUAGE sql IMMUTABLE;

public jest to schemat, w którym zainstalowano rozszerzenie (public jest domyślne).

wcześniej dodałem SET search_path = public, pg_temp do funkcji-dopóki nie odkryłem, że słownik może być również klasyfikowany według schematu, , który obecnie (pg 10) nie jest udokumentowany . Ta wersja jest nieco krótsza i około dwa razy szybsza w moich testach na pg 9.5 i pg 10.

Zaktualizowana wersja nadal nie pozwala funkcji inlining ponieważ funkcje zadeklarowane IMMUTABLE nie mogą wywoływać funkcji nieujemnych w ciele, aby na to pozwolić. Nie ma znaczenia dla wydajności, podczas gdy korzystamy z indeks wyrażeń na tej funkcji IMMUTABLE:

CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));

Bezpieczeństwo programów klienckich zostało zaostrzone w Postgres 10.3 / 9.6.8 itd. Musisz aby schema-qualify function and dictionary były pokazane, gdy są używane w dowolnych indeksach. Zobacz:

Dostosuj swoje zapytania do indeksu (aby planista zapytań mógł go użyć):

SELECT * FROM users
WHERE  f_unaccent(name) = f_unaccent('João');

Nie potrzebujesz funkcji w odpowiednim wyrażeniu. Możesz dostarczyć unactened ciągi jak 'Joao' bezpośrednio.

Ligatury

W Postgres 9.5 lub starszych ligatury takie jak' œ 'lub' ß ' muszą być rozszerzane ręcznie (jeśli tego potrzebujesz), ponieważ unaccent() zawsze zastępuje a single letter:

SELECT unaccent('Œ Æ œ æ ß');

unaccent
----------
E A e a S

Pokochasz ten update to unaccent W Postgres 9.6:

Rozszerz contrib/unaccent standardowy plik unaccent.rules, aby obsłużyć wszystkie znaki diakrytyczne znane Unicode i rozwiń ligatury poprawnie (Thomas Munro, Léonard Benedetti)

/ Align = "left" / Teraz otrzymujemy:
SELECT unaccent('Œ Æ œ æ ß');

unaccent
----------
OE AE oe ae ss

Dopasowanie wzorca

Dla LIKE lub ILIKE z dowolnymi wzorami, połącz to z moduł pg_trgm W PostgreSQL 9.1 lub nowszym. Utwórz indeks wyrażenia trygram GIN (zwykle preferowany) lub GIST. Przykład dla ginu:

CREATE INDEX users_unaccent_name_trgm_idx ON users
USING gin (f_unaccent(name) gin_trgm_ops);

Może być używany do zapytań typu:

SELECT * FROM users
WHERE  f_unaccent(name) LIKE ('%' || f_unaccent('João') || '%');
Indeksy GIN i GIST są droższe w utrzymaniu niż zwykłe btree:]}

Istnieją prostsze rozwiązania dla tylko lewych zakotwiczonych wzorów. Więcej o dopasowywaniu wzorów i wydajność:

pg_trgm zawiera również użyteczne operatory dla "podobieństwa" (%) i "odległości" (<->).

Indeksy Trygramowe obsługują również proste wyrażenia regularne z ~ et al. and case niewrażliwe wzór pasujący do ILIKE:

 149
Author: Erwin Brandstetter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-13 08:36:26

Jestem prawie pewien, że PostgreSQL opiera się na bazowym systemie operacyjnym do sortowania. To nie obsługuje tworzenie nowych zestawień i dostosowywanie zestawień. Nie jestem pewien, ile to może być dla Ciebie pracy. (Może być sporo.)

 2
Author: Mike Sherrill 'Cat Recall',
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-12 21:54:48

Nie, PostgreSQL nie obsługuje kolacji w tym sensie

PostgreSQL nie obsługuje takich zestawień (nieczułych lub nie), ponieważ żadne porównanie nie może zwrócić równości, chyba że rzeczy są binarne-równe. To dlatego, że wewnętrznie wprowadzi wiele złożoności dla rzeczy takich jak indeks hash. Z tego powodu zestawienia w ich ścisłym znaczeniu wpływają tylko na uporządkowanie , a nie na równość.

Obejścia

Full-Text-Search Dictionary that / Align= "left" /

Dla FTS można zdefiniować własny słownik używając unaccent,

CREATE EXTENSION unaccent;

CREATE TEXT SEARCH CONFIGURATION mydict ( COPY = simple );
ALTER TEXT SEARCH CONFIGURATION mydict
  ALTER MAPPING FOR hword, hword_part, word
  WITH unaccent, simple;

Które można następnie indeksować indeksem funkcjonalnym,

-- Just some sample data...
CREATE TABLE myTable ( myCol )
  AS VALUES ('fóó bar baz'),('qux quz');

-- No index required, but feel free to create one
CREATE INDEX ON myTable
  USING GIST (to_tsvector('mydict', myCol));

Możesz teraz odpytywać to bardzo prosto

SELECT *
FROM myTable
WHERE to_tsvector('mydict', myCol) @@ 'foo & bar'

    mycol    
-------------
 fóó bar baz
(1 row)

Zobacz też

/ Align = "left" /

The unaccent Moduł może być również używany samodzielnie bez integracji FTS, do tego sprawdzenia out Erwin ' s answer

 1
Author: Evan Carroll,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-30 01:52:08