PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Unterstützt PostgreSQL akzentunabhängige Sortierungen?

Verwenden Sie das Unaccent-Modul dafür - was völlig anders ist als das, worauf Sie verlinken.

unaccent ist ein Textsuchwörterbuch, das Akzente (diakritische Zeichen) aus Lexemen entfernt.

Einmal pro Datenbank installieren mit:

CREATE EXTENSION unaccent;

Wenn Sie eine Fehlermeldung erhalten wie:

ERROR: could not open extension control file
"/usr/share/postgresql/<version>/extension/unaccent.control": No such file or directory

Installieren Sie das Contrib-Paket auf Ihrem Datenbankserver, wie in dieser zugehörigen Antwort beschrieben:

  • Fehler beim Erstellen einer Erweiterung ohne Akzent auf PostgreSQL

Sie stellt unter anderem die Funktion unaccent() zur Verfügung Sie können mit Ihrem Beispiel verwenden (wobei LIKE scheint nicht erforderlich).

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

Index

Um einen Index für diese Art von Abfrage zu verwenden, erstellen Sie einen Index für den Ausdruck. Allerdings , Postgres akzeptiert nur IMMUTABLE Funktionen für Indizes. Wenn eine Funktion für dieselbe Eingabe ein anderes Ergebnis zurückgeben kann, könnte der Index stillschweigend unterbrochen werden.

unaccent() nur STABLE nicht IMMUTABLE

Leider unaccent() ist nur STABLE , nicht IMMUTABLE . Laut diesem Thread zu pgsql-bugs liegt dies an drei Gründe:

  1. Es hängt vom Verhalten eines Wörterbuchs ab.
  2. Es besteht keine festverdrahtete Verbindung zu diesem Wörterbuch.
  3. Es hängt also auch vom aktuellen search_path ab , die sich leicht ändern können.

Einige Tutorials im Internet weisen darauf hin, die Volatilität der Funktion einfach auf IMMUTABLE zu ändern . Diese Brute-Force-Methode kann unter bestimmten Bedingungen brechen.

Andere schlagen ein einfaches IMMUTABLE vor Wrapper-Funktion (wie ich es selbst in der Vergangenheit getan habe).

Es gibt eine anhaltende Debatte darüber, ob man die Variante mit zwei Parametern IMMUTABLE machen soll die das verwendete Wörterbuch explizit deklariert. Lesen Sie hier oder hier.

Eine weitere Alternative wäre dieses Modul mit einem IMMUTABLE unaccent() Funktion von Musicbrainz, bereitgestellt auf Github. Habe es selbst nicht getestet. Ich glaube, mir ist eine bessere Idee eingefallen :

Das Beste für jetzt

Dieser Ansatz ist effizienter als andere Lösungen, die herumschwirren, und sicherer .
Erstellen Sie einen IMMUTABLE SQL-Wrapper-Funktion, die das Zwei-Parameter-Formular mit festverdrahteter Schema-qualifizierter Funktion und Wörterbuch ausführt.

Da das Verschachteln einer nicht unveränderlichen Funktion das Funktions-Inlining deaktivieren würde, basieren Sie es auf einer Kopie der C-Funktion, die (gefälscht) als IMMUTABLE deklariert ist auch. Es ist nur Zweck soll im SQL-Funktionswrapper verwendet werden. Nicht für die alleinige Verwendung gedacht.

Die Komplexität ist erforderlich, da es keine Möglichkeit gibt, das Wörterbuch in der Deklaration der C-Funktion fest zu verdrahten. (Musste den C-Code selbst hacken.) Die SQL-Wrapper-Funktion tut dies und ermöglicht das Inlining von und für beide Funktionen Ausdrucksindizes.

CREATE OR REPLACE FUNCTION public.immutable_unaccent(regdictionary, text)
  RETURNS text LANGUAGE c IMMUTABLE PARALLEL SAFE STRICT AS
'$libdir/unaccent', 'unaccent_dict';

CREATE OR REPLACE FUNCTION public.f_unaccent(text)
  RETURNS text LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
$func$
SELECT public.immutable_unaccent(regdictionary 'public.unaccent', $1)
$func$;

Legen Sie PARALLEL SAFE ab von beiden Funktionen für Postgres 9.5 oder älter.

public Dabei handelt es sich um das Schema, in dem Sie die Erweiterung installiert haben (public ist die Standardeinstellung).

Die explizite Typdeklaration (regdictionary ) wehrt hypothetische Angriffe mit überladenen Varianten der Funktion durch böswillige Benutzer ab.

Früher habe ich eine Wrapper-Funktion basierend auf STABLE befürwortet Funktion unaccent() mit dem Unakzent-Modul ausgeliefert. Diese deaktivierte Funktion Inlining. Diese Version wird zehnmal schneller ausgeführt als die einfache Wrapper-Funktion, die ich hier früher hatte.
Und das war bereits doppelt so schnell wie die erste Version, die SET search_path = public, pg_temp hinzufügte zur Funktion - bis ich entdeckte, dass das Wörterbuch auch Schema-qualifiziert werden kann. Immer noch (Postgres 12) nicht zu offensichtlich aus der Dokumentation.

Wenn Ihnen die notwendigen Privilegien zum Erstellen von C-Funktionen fehlen, sind Sie wieder bei der zweitbesten Implementierung:einem IMMUTABLE Funktionswrapper um den STABLE unaccent() vom Modul bereitgestellte Funktion:

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 PARALLEL SAFE STRICT;

Schließlich der Ausdrucksindex Abfragen schnell zu machen :

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

Denken Sie daran, Indexe neu zu erstellen Einbeziehen dieser Funktion nach jeder Änderung an Funktion oder Wörterbuch, wie ein direktes Major-Release-Upgrade, das keine Indizes neu erstellen würde. Alle neueren Hauptversionen enthielten Aktualisierungen für unaccent Modul.

Passen Sie Abfragen an den Index an (damit der Abfrageplaner ihn verwendet):

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

Sie brauchen die Funktion nicht im richtigen Ausdruck. Dort können Sie auch Zeichenfolgen ohne Akzent wie 'Joao' angeben direkt.

Die schnellere Funktion führt nicht zu viel schnelleren Abfragen mit dem Ausdrucksindex . Das arbeitet mit vorberechneten Werten und ist bereits sehr schnell. Aber die Indexpflege und Abfragen, die den Index nicht verwenden, profitieren.

Die Sicherheit für Client-Programme wurde mit Postgres 10.3 / 9.6.8 usw. verschärft. Sie brauchen zur Schemaqualifizierung von Funktions- und Wörterbuchnamen, wie gezeigt, wenn sie in Indizes verwendet werden. Siehe:

  • 'Textsuche-Wörterbuch „unaccent“ existiert nicht'-Einträge im Postgres-Protokoll, angeblich während der automatischen Analyse

Ligaturen

In Postgres 9.5 oder älter Ligaturen wie 'Œ' oder 'ß' müssen manuell erweitert werden (falls nötig), da unaccent() ersetzt immer ein single Buchstabe:

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

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

Sie werden dieses Update lieben, um in Postgres 9.6 keine Akzente zu setzen :

Erweitern Sie contrib/unaccent 's Standard unaccent.rules Datei, um alle in Unicode bekannten diakritischen Zeichen zu verarbeiten und Ligaturen korrekt zu erweitern (Thomas Munro, Léonard Benedetti)

Fette Hervorhebung von mir. Jetzt erhalten wir:

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

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

Musterabgleich

Für LIKE oder ILIKE mit beliebigen Mustern kombinieren Sie dies mit dem Modul pg_trgm in PostgreSQL 9.1 oder höher. Erstellen Sie einen Trigramm-GIN- (normalerweise bevorzugt) oder GIST-Ausdrucksindex. Beispiel für GIN:

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

Kann für Abfragen verwendet werden wie:

SELECT * FROM users
WHERE  f_unaccent(name) LIKE ('%' || f_unaccent('João') || '%');

GIN- und GIST-Indizes sind teurer in der Wartung als einfache btree:

  • Unterschied zwischen GiST- und GIN-Index

Es gibt einfachere Lösungen für nur links verankerte Muster. Weitere Informationen zu Musterabgleich und Leistung:

  • Mustervergleich mit LIKE, SIMILAR TO oder regulären Ausdrücken in PostgreSQL

pg_trgm bietet auch nützliche Operatoren für "Ähnlichkeit" (% ) und „Entfernung“ (<-> ).

Trigram-Indizes unterstützen auch einfache reguläre Ausdrücke mit ~ et al. und Groß- und Kleinschreibung Mustervergleich mit ILIKE :

  • PostgreSQL-Akzent + Suche ohne Berücksichtigung der Groß-/Kleinschreibung