Verwenden Sie dazu das Modul ohne Akzent - das völlig anders ist als das, auf das 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 Antwort beschrieben:
Unter anderem bietet es die Funktion, die unaccent()
Sie für Ihr Beispiel verwenden können (wo dies LIKE
nicht benötigt wird).
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 . Postgres akzeptiert jedoch nur IMMUTABLE
Funktionen für Indizes. Wenn eine Funktion für dieselbe Eingabe ein anderes Ergebnis zurückgeben kann, kann der Index stillschweigend unterbrochen werden.
unaccent()
nur STABLE
nichtIMMUTABLE
Leider unaccent()
ist nur STABLE
nicht IMMUTABLE
. Laut diesem Thread zu pgsql-Bugs hat dies drei Gründe:
- Dies hängt vom Verhalten eines Wörterbuchs ab.
- Es besteht keine fest verdrahtete Verbindung zu diesem Wörterbuch.
- Es kommt also auch auf den Strom an
search_path
, der sich leicht ändern kann.
Einige Tutorials im Web weisen an, nur die Funktionsvolatilität auf zu ändernIMMUTABLE
. Diese Brute-Force-Methode kann unter bestimmten Bedingungen brechen.
Andere schlagen eine einfache IMMUTABLE
Wrapper-Funktion vor (wie ich es selbst in der Vergangenheit getan habe).
Es gibt eine anhaltende Debatte darüber, ob die Variante mit zwei Parametern erstellt werden soll, IMMUTABLE
die das verwendete Wörterbuch explizit deklarieren. Lesen Sie hier oder hier .
Eine andere Alternative wäre dieses Modul mit einer IMMUTABLE- unaccent()
Funktion von Musicbrainz , die auf Github bereitgestellt wird. Habe es nicht selbst getestet. Ich denke, ich habe eine bessere Idee :
Am besten für jetzt
Dieser Ansatz ist effizienter als andere Lösungen und sicherer .
Erstellen Sie eine IMMUTABLE
SQL-Wrapper-Funktion, die das Zwei-Parameter-Formular mit einer fest verdrahteten schemaqualifizierten Funktion und einem Wörterbuch ausführt.
Da das Verschachteln einer nicht unveränderlichen Funktion das Inlining von Funktionen deaktivieren würde, basieren Sie auf einer Kopie der ebenfalls deklarierten (gefälschten) C-Funktion IMMUTABLE
. Der einzige Zweck besteht darin, im SQL-Funktions-Wrapper verwendet zu werden. Nicht für den alleinigen Gebrauch gedacht.
Die Raffinesse ist erforderlich, da das Wörterbuch in der Deklaration der C-Funktion nicht fest verdrahtet werden kann. (Müsste den C-Code selbst hacken.) Die SQL-Wrapper-Funktion erledigt dies und ermöglicht sowohl Inlining- als auch 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$;
Löschen Sie PARALLEL SAFE
beide Funktionen für Postgres 9.5 oder älter.
public
Dies ist das Schema, in dem Sie die Erweiterung installiert haben (dies public
ist die Standardeinstellung).
Die explizite Typdeklaration ( regdictionary
) schützt vor hypothetischen Angriffen mit überladenen Varianten der Funktion durch böswillige Benutzer.
Früher habe ich mich für eine Wrapper - Funktion auf der Grundlage der STABLE
Funktion unaccent()
mit dem unaccent Modul ausgeliefert. Diese deaktivierte Funktion Inlining . Diese Version wird zehnmal schneller ausgeführt als die einfache Wrapper-Funktion, die ich zuvor hier hatte.
Und das war schon doppelt so schnell wie die erste Version, die SET search_path = public, pg_temp
die Funktion erweitert hat - bis ich herausfand, dass das Wörterbuch auch schemaqualifiziert werden kann. Dennoch (Postgres 12) aus der Dokumentation nicht allzu offensichtlich.
Wenn Sie nicht über die erforderlichen Berechtigungen zum Erstellen von C-Funktionen verfügen, kehren Sie zur zweitbesten Implementierung zurück: Ein IMMUTABLE
Funktionsumbruch um die 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 , um Abfragen schnell zu machen :
CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));
Denken Sie daran , nach jeder Änderung der Funktion oder des Wörterbuchs Indizes neu zu erstellen, die diese Funktion betreffen, z. B. ein direktes Upgrade der Hauptversion, bei dem keine Indizes neu erstellt werden. Die letzten Hauptversionen hatten alle Updates für das 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'
direkt angeben .
Die schnellere Funktion übersetzt nicht in viel schnellere Abfragen unter Verwendung des Ausdrucksindex . Das arbeitet mit vorberechneten Werten und ist schon sehr schnell. Aber Indexpflege und Abfragen, die den Indexvorteil nicht nutzen.
Die Sicherheit für Client-Programme wurde mit Postgres 10.3 / 9.6.8 usw. verschärft. Sie müssen die Funktion und den Wörterbuchnamen für das Schema qualifizieren, wie bei der Verwendung in Indizes gezeigt. Sehen:
Ligaturen
In Postgres 9.5 oder älteren müssen Ligaturen wie 'Œ' oder 'ß' manuell erweitert werden (falls erforderlich), da unaccent()
immer ein einzelner Buchstabe ersetzt wird:
SELECT unaccent('Œ Æ œ æ ß');
unaccent
----------
E A e a S
Sie werden dieses Update lieben, um in Postgres 9.6 nicht zu akzentuieren :
Erweitern Sie contrib/unaccent
die Standarddatei unaccent.rules
, um alle Unicode bekannten Diakritika zu verarbeiten, und erweitern Sie die Ligaturen korrekt (Thomas Munro, Léonard Benedetti).
Meine kühne Betonung. Jetzt bekommen wir:
SELECT unaccent('Œ Æ œ æ ß');
unaccent
----------
OE AE oe ae ss
Mustervergleich
Kombinieren Sie dies für LIKE
oder ILIKE
mit beliebigen Mustern mit dem Modul pg_trgm
in PostgreSQL 9.1 oder höher. Erstellen Sie einen Trigramm-GIN- (normalerweise vorzuziehen) 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 wie verwendet werden:
SELECT * FROM users
WHERE f_unaccent(name) LIKE ('%' || f_unaccent('João') || '%');
Die Pflege von GIN- und GIST-Indizes ist teurer als die von Btree:
Es gibt einfachere Lösungen für nur links verankerte Muster. Weitere Informationen zu Mustervergleich und Leistung:
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 Muster, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird ILIKE
: