sql >> Database teknologi >  >> RDS >> PostgreSQL

Understøtter PostgreSQL accentufølsomme sammenstillinger?

Brug unaccent-modulet for det - som er helt anderledes end det du linker til.

unaccent er en tekstsøgningsordbog, der fjerner accenter (diakritiske tegn) fra leksemer.

Installer én gang pr. database med:

CREATE EXTENSION unaccent;

Hvis du får en fejl som:

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

Installer bidragspakken på din databaseserver som instrueret i dette relaterede svar:

  • Fejl ved oprettelse af unaccent-udvidelse på PostgreSQL

Den giver blandt andet funktionen unaccent() du kan bruge med dit eksempel (hvor LIKE synes ikke nødvendigt).

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

Indeks

For at bruge et indeks til den slags forespørgsler skal du oprette et indeks på udtrykket. Men , Postgres accepterer kun IMMUTABLE funktioner til indekser. Hvis en funktion kan returnere et andet resultat for det samme input, kan indekset gå i stykker.

unaccent() kun STABIL ikke IMMUTABLE

Desværre, unaccent() er kun STABIL , ikke IMMUTABLE . Ifølge denne tråd om pgsql-bugs, skyldes dette tre årsager:

  1. Det afhænger af opførselen af ​​en ordbog.
  2. Der er ingen fastkablet forbindelse til denne ordbog.
  3. Det afhænger derfor også af den aktuelle søgesti , som nemt kan ændres.

Nogle tutorials på nettet instruerer i at ændre funktionsvolatiliteten til IMMUTABLE . Denne brute-force-metode kan gå i stykker under visse forhold.

Andre foreslår en simpel IMMUTABLE indpakningsfunktion (som jeg gjorde selv tidligere).

Der er en løbende debat, om man skal lave varianten med to parametre IMMUTABLE som erklærer den brugte ordbog eksplicit. Læs her eller her.

Et andet alternativ ville være dette modul med en IMMUTABLE unaccent() funktion af Musicbrainz, leveret på Github. Har ikke selv testet det. Jeg tror, ​​jeg har fundet på en bedre idé :

Bedst lige nu

Denne tilgang er mere effektiv som andre løsninger, der flyder rundt, og sikrere .
Opret en IMMUTABLE SQL-indpakningsfunktion, der udfører formularen med to parametre med fastkablet skema-kvalificeret funktion og ordbog.

Da indlejring af en ikke-foranderlig funktion ville deaktivere funktions-inlining, skal du basere den på en kopi af C-funktionen, (falsk) erklæret IMMUTABLE såvel. Det er eneste formålet er at blive brugt i SQL-funktionsindpakningen. Ikke beregnet til at blive brugt alene.

Sofistikeringen er nødvendig, da der ikke er nogen måde at fastlægge ordbogen i erklæringen om C-funktionen. (Ville kræve at hacke selve C-koden.) SQL-indpakningsfunktionen gør det og tillader både funktion inlining og udtryksindekser.

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$;

Slip PARALLEL SAFE fra begge funktioner til Postgres 9.5 eller ældre.

offentlig er det skema, hvor du installerede udvidelsen (public er standard).

Den eksplicitte typeerklæring (regdictionary ) forsvarer sig mod hypotetiske angreb med overbelastede varianter af funktionen fra ondsindede brugere.

Tidligere gik jeg ind for en indpakningsfunktion baseret på STABLE funktion unaccent() leveres med unaccent-modulet. Den deaktiverede funktion inlining. Denne version udføres ti gange hurtigere end den simple indpakningsfunktion, jeg havde her tidligere.
Og det var allerede dobbelt så hurtigt som den første version, der tilføjede SET search_path =public, pg_temp til funktionen - indtil jeg opdagede, at ordbogen også kan skema-kvalificeres. Stadig (Postgres 12) ikke for tydeligt ud fra dokumentation.

Hvis du mangler de nødvendige privilegier til at oprette C-funktioner, er du tilbage til den næstbedste implementering:En IMMUTABLE funktionsindpakning omkring STABLE unaccent() funktion leveret af modulet:

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;

Til sidst udtryksindekset for at foretage forespørgsler hurtigt :

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

Husk at genoprette indekser involverer denne funktion efter enhver ændring af funktion eller ordbog, som en større udgivelsesopgradering på stedet, der ikke ville genskabe indekser. De seneste større udgivelser havde alle opdateringer til unaccent modul.

Tilpas forespørgsler til at matche indekset (så forespørgselsplanlæggeren bruger det):

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

Du behøver ikke funktionen i det rigtige udtryk. Der kan du også levere strenge uden accent som 'Joao' direkte.

Den hurtigere funktion oversættes ikke til meget hurtigere forespørgsler ved hjælp af udtryksindekset . Det fungerer på forudberegnede værdier og er allerede meget hurtigt. Men indeksvedligeholdelse og forespørgsler, der ikke bruger indeksfordel.

Sikkerheden for klientprogrammer er blevet skærpet med Postgres 10.3 / 9.6.8 osv. Du bruger for at skemakvalificere funktion og ordbogsnavn som vist, når det bruges i alle indekser. Se:

  • 'tekstsøgeordbog "uaccent" findes ikke'-indgange i postgres-log, angiveligt under automatisk analyse

Ligaturer

I Postgres 9.5 eller ældre ligaturer som 'Œ' eller 'ß' skal udvides manuelt (hvis du har brug for det), da unaccent() erstatter altid en enkelt brev:

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

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

Du vil elske denne opdatering til unaccent i Postgres 9.6 :

Udvid contrib/unaccent 's standard unaccent.rules fil til at håndtere alle diakritiske punkter, der er kendt af Unicode, og udvide ligaturer korrekt (ThomasMunro, Léonard Benedetti)

Fed fremhævelse min. Nu får vi:

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

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

Mønstertilpasning

For LIKE eller ILIKE med vilkårlige mønstre, kombiner dette med modulet pg_trgm i PostgreSQL 9.1 eller nyere. Opret et trigram GIN (typisk at foretrække) eller GIST udtryksindeks. Eksempel på GIN:

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

Kan bruges til forespørgsler som:

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

GIN- og GIST-indekser er dyrere at vedligeholde end almindelige btree:

  • Forskel mellem GiST og GIN-indeks

Der er enklere løsninger til netop venstreforankrede mønstre. Mere om mønstertilpasning og ydeevne:

  • Mønstermatching med LIKE, SIMILAR TO eller regulære udtryk i PostgreSQL

pg_trgm giver også nyttige operatorer for "lighed" (% ) og "afstand" (<-> ).

Trigramindekser understøtter også simple regulære udtryk med ~ et al. og ufølsomme mellem store og små bogstaver mønster, der matcher med ILIKE :

  • PostgreSQL-accent + versal-ufølsom søgning


  1. Skal skalarvariabel @Id erklæres?

  2. Hvordan Random() virker i PostgreSQL

  3. Hvordan bruger man COUNT i SQL?

  4. Skal vi angive ikke null for primærnøgle? Oracle/SQL