sql >> Databasteknik >  >> RDS >> PostgreSQL

Stöder PostgreSQL accentokänsliga kollationer?

Använd oaccentmodulen för det - som är helt annorlunda än det du länkar till.

unaccent är en textsökningsordbok som tar bort accenter (diakritiska tecken) från lexem.

Installera en gång per databas med:

CREATE EXTENSION unaccent;

Om du får ett felmeddelande som:

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

Installera bidragspaketet på din databasserver enligt anvisningarna i detta relaterade svar:

  • Fel vid skapande av unaccent-tillägg på PostgreSQL

Den tillhandahåller bland annat funktionen unaccent() du kan använda med ditt exempel (där LIKE verkar inte behövas).

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

Index

För att använda ett index för den typen av fråga, skapa ett index på uttrycket. Men , Postgres accepterar endast IMMUTABLE funktioner för index. Om en funktion kan returnera ett annat resultat för samma indata, kan indexet gå sönder.

unaccent() endast STABIL inte IMMUTABLE

Tyvärr, unaccent() är bara STABIL , inte IMMUTABLE . Enligt denna tråd om pgsql-bugs beror detta på tre skäl:

  1. Det beror på beteendet hos en ordbok.
  2. Det finns ingen fast anslutning till denna ordbok.
  3. Det beror därför också på den aktuella sökvägen , som lätt kan ändras.

Vissa tutorials på webben instruerar att bara ändra funktionsvolatiliteten till IMMUTABLE . Denna brute-force-metod kan gå sönder under vissa förhållanden.

Andra föreslår en enkel IMMUTABLE omslagsfunktion (som jag gjorde själv tidigare).

Det pågår en diskussion om man ska göra varianten med två parametrar IMMUTABLE som deklarerar den använda ordboken uttryckligen. Läs här eller här.

Ett annat alternativ skulle vara den här modulen med en IMMUTABLE unaccent() funktion av Musicbrainz, tillhandahållen på Github. Har inte testat det själv. Jag tror att jag har kommit på en bättre idé :

Bäst för tillfället

Detta tillvägagångssätt är effektivare som andra lösningar som flyter runt och säkrare .
Skapa en IMMUTABLE SQL-omslagsfunktion som exekverar formuläret med två parametrar med fast schemakvalificerad funktion och ordbok.

Eftersom kapsling av en oföränderlig funktion skulle inaktivera funktionsinläggning, basera den på en kopia av C-funktionen, (falsk) förklarad IMMUTABLE också. Det är endast syftet är att användas i SQL-funktionsomslaget. Inte avsett att användas på egen hand.

Det sofistikerade behövs eftersom det inte finns något sätt att hårdkoppla ordboken i deklarationen av C-funktionen. (Kräver att hacka själva C-koden.) SQL-omslagsfunktionen gör det och tillåter både funktion inlining och uttrycksindex.

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

Släpp PARALLEL SAFE från båda funktionerna för Postgres 9.5 eller äldre.

offentlig är schemat där du installerade tillägget (public är standard).

Den explicita typdeklarationen (regdictionary ) försvarar sig mot hypotetiska attacker med överbelastade varianter av funktionen från illvilliga användare.

Tidigare förespråkade jag en omslagsfunktion baserad på STABLE funktion unaccent() levereras med unaccentmodulen. Den avaktiverade funktionen inlining. Den här versionen körs tio gånger snabbare än den enkla omslagsfunktionen jag hade här tidigare.
Och det var redan dubbelt så snabbt som den första versionen som lade till SET search_path =public, pg_temp till funktionen - tills jag upptäckte att ordboken också kan schemakvalificeras. Fortfarande (Postgres 12) inte alltför tydligt från dokumentationen.

Om om du saknar de nödvändiga privilegierna för att skapa C-funktioner är du tillbaka till den näst bästa implementeringen:En IMMUTABLE funktionsomslag runt STABLE unaccent() funktion som tillhandahålls av modulen:

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;

Slutligen, uttrycksindex för att göra frågor snabbt :

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

Kom ihåg att återskapa index involverar den här funktionen efter någon ändring av funktion eller ordbok, som en större versionsuppgradering på plats som inte skulle återskapa index. De senaste större utgåvorna hade alla uppdateringar för unaccent modul.

Anpassa frågor så att de matchar indexet (så att frågeplaneraren använder det):

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

Du behöver inte funktionen i rätt uttryck. Där kan du också ange strängar utan accent som 'Joao' direkt.

Den snabbare funktionen översätts inte till mycket snabbare frågor med uttrycksindex . Det fungerar på förberäknade värden och är redan väldigt snabbt. Men index underhåll och frågor som inte använder indexförmånen.

Säkerheten för klientprogram har skärpts med Postgres 10.3 / 9.6.8 etc. Du behöver för att schemakvalificera funktion och ordboksnamn som visas när det används i några index. Se:

  • 'textsökningsordbok "oaccent" finns inte'-poster i postgres-loggen, förmodligen under automatisk analys

Ligaturer

I Postgres 9.5 eller äldre ligaturer som 'Œ' eller 'ß' måste utökas manuellt (om du behöver det), eftersom unaccent() ersätter alltid en singel brev:

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

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

Du kommer att älska den här uppdateringen så att den inte blir tydligare i Postgres 9.6 :

Förläng contrib/unaccent s standard unaccent.rules fil för att hantera alla diakritiker som är kända för Unicode, och expandera ligaturer korrekt (ThomasMunro, Léonard Benedetti)

Djärv betoning min. Nu får vi:

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

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

Mönstermatchning

För GILLA eller ILIKE med godtyckliga mönster, kombinera detta med modulen pg_trgm i PostgreSQL 9.1 eller senare. Skapa ett trigram-GIN (vanligtvis att föredra) eller GIST-uttrycksindex. Exempel på GIN:

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

Kan användas för frågor som:

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

GIN- och GIST-index är dyrare att underhålla än vanligt btree:

  • Skillnad mellan GiST och GIN-index

Det finns enklare lösningar för just vänsterförankrade mönster. Mer om mönstermatchning och prestanda:

  • Mönstermatchning med LIKE, SIMILAR TO eller reguljära uttryck i PostgreSQL

pg_trgm tillhandahåller också användbara operatorer för "likhet" (% ) och "avstånd" (<-> ).

Trigramindex stöder också enkla reguljära uttryck med ~ et al. och skiftlägesokänslig mönstermatchning med ILIKE :

  • PostgreSQL-accent + skiftlägesokänslig sökning


  1. Hur kan jag använda förberedda uttalanden i CodeIgniter

  2. DATABASE() – Hämta det aktuella databasnamnet i MySQL

  3. Vad händer egentligen med det sökandet?

  4. Pivotera på flera kolumner med Tablefunc