sql >> Database >  >> RDS >> PostgreSQL

Ondersteunt PostgreSQL accentongevoelige sorteringen?

Gebruik de unaccent module daarvoor - wat totaal anders is dan waarnaar u linkt.

unaccent is een tekstzoekwoordenboek dat accenten (diakritische tekens) uit lexemen verwijdert.

Installeer eenmaal per database met:

CREATE EXTENSION unaccent;

Als u een foutmelding krijgt zoals:

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

Installeer het contrib-pakket op uw databaseserver zoals aangegeven in dit gerelateerde antwoord:

  • Fout bij het maken van een extensie zonder accent op PostgreSQL

Het biedt onder andere de functie unaccent() u kunt gebruiken met uw voorbeeld (waar LIKE lijkt niet nodig).

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

Index

Als u een index voor dat soort query wilt gebruiken, maakt u een index voor de expressie. Echter , Postgres accepteert alleen IMMUTABLE functies voor indexen. Als een functie een ander resultaat voor dezelfde invoer kan retourneren, kan de index stilletjes breken.

unaccent() alleen STABLE niet IMMUTABLE

Helaas, unaccent() is alleen STABLE , niet IMMUTABLE . Volgens deze thread over pgsql-bugs is dit te wijten aan drie redenen:

  1. Het hangt af van het gedrag van een woordenboek.
  2. Er is geen vaste verbinding met dit woordenboek.
  3. Het hangt daarom ook af van het huidige search_path , die gemakkelijk kan veranderen.

Sommige tutorials op het web geven de instructie om de functievolatiliteit gewoon te veranderen in IMMUTABLE . Deze brute-force-methode kan onder bepaalde omstandigheden breken.

Anderen suggereren een eenvoudige IMMUTABLE wrapper-functie (zoals ik in het verleden zelf deed).

Er is een voortdurende discussie of de variant met twee parameters IMMUTABLE . moet worden gemaakt die het gebruikte woordenboek expliciet declareert. Lees hier of hier.

Een ander alternatief zou deze module zijn met een IMMUTABLE unaccent() functie door Musicbrainz, geleverd op Github. Heb het zelf niet getest. Ik denk dat ik een beter idee heb bedacht :

Het beste voor nu

Deze aanpak is efficiënter omdat andere oplossingen rondzweven, en veiliger .
Maak een IMMUTABLE SQL-wrapperfunctie die het formulier met twee parameters uitvoert met een vast bedrade schema-gekwalificeerde functie en woordenboek.

Aangezien het nesten van een niet-onveranderlijke functie functie-inlining zou uitschakelen, baseer het dan op een kopie van de C-functie, (nep) verklaard IMMUTABLE ook. Het is alleen doel is om te worden gebruikt in de SQL-functiewrapper. Niet bedoeld om op zichzelf te worden gebruikt.

De verfijning is nodig omdat er geen manier is om het woordenboek vast te leggen in de verklaring van de C-functie. (Hiervoor zou de C-code zelf moeten worden gehackt.) De SQL-wrapperfunctie doet dat en staat zowel functie-inlining en toe expressie-indexen.

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

Drop PARALLEL SAFE van beide functies voor Postgres 9.5 of ouder.

public zijnde het schema waar je de extensie hebt geïnstalleerd (public is de standaardinstelling).

De expliciete typedeclaratie (regdictionary ) verdedigt tegen hypothetische aanvallen met overbelaste varianten van de functie door kwaadwillende gebruikers.

Eerder pleitte ik voor een wrapper-functie op basis van de STABLE functie unaccent() geleverd met de unaccent-module. Die uitgeschakelde functie inlining. Deze versie werkt tien keer sneller dan de eenvoudige wrapper-functie die ik hier eerder had.
En dat was al twee keer zo snel als de eerste versie die SET search_path = public, pg_temp toevoegde aan de functie - totdat ik ontdekte dat het woordenboek ook schema-gekwalificeerd kan zijn. Toch (Postgres 12) niet al te duidelijk uit documentatie.

Als je mist de nodige privileges om C-functies te creëren, je bent terug bij de op één na beste implementatie:een IMMUTABLE functieomslag rond de STABLE unaccent() functie geleverd door de module:

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;

Ten slotte de expressie-index om snel vragen te stellen :

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

Vergeet niet om indexen opnieuw te maken waarbij deze functie wordt gebruikt na elke wijziging aan functie of woordenboek, zoals een interne grote release-upgrade die geen indexen opnieuw zou creëren. Recente grote releases hadden allemaal updates voor de unaccent module.

Pas zoekopdrachten aan zodat ze overeenkomen met de index (zodat de queryplanner deze zal gebruiken):

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

U hebt de functie niet in de juiste expressie nodig. Daar kun je ook tekenreeksen zonder accenten opgeven, zoals 'Joao' rechtstreeks.

De snellere functie vertaalt zich niet in veel snellere zoekopdrachten met behulp van de expressie-index . Dat werkt op vooraf berekende waarden en is al erg snel. Maar indexonderhoud en zoekopdrachten gebruiken het indexvoordeel niet.

Beveiliging voor clientprogramma's is aangescherpt met Postgres 10.3 / 9.6.8 enz. U nodig om de functie en de naam van het woordenboek volgens schema te kwalificeren, zoals aangetoond bij gebruik in indexen. Zie:

  • 'tekstzoekwoordenboek "unaccent" bestaat niet'-items in postgres-log, vermoedelijk tijdens automatische analyse

Ligaturen

In Postgres 9.5 of ouder ligaturen zoals 'Œ' of 'ß' moeten handmatig worden uitgebreid (als je dat nodig hebt), aangezien unaccent() vervangt altijd een enkele brief:

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

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

Je zult genieten van deze update zonder accent in Postgres 9.6 :

Verleng contrib/unaccent 's standaard unaccent.rules bestand om alle diakritische tekens die bekend zijn bij Unicode te verwerken en ligaturen correct uit te breiden (ThomasMunro, Léonard Benedetti)

Vetgedrukte nadruk van mij. Nu krijgen we:

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

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

Patroon komt overeen

Voor LIKE of ILIKE met willekeurige patronen, combineer dit met de module pg_trgm in PostgreSQL 9.1 of hoger. Maak een trigram GIN (meestal de voorkeur) of GIST-expressie-index. Voorbeeld voor GIN:

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

Kan worden gebruikt voor vragen als:

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

GIN- en GIST-indexen zijn duurder in onderhoud dan gewone btree:

  • Verschil tussen GiST en GIN-index

Er zijn eenvoudigere oplossingen voor alleen links verankerde patronen. Meer over patroonafstemming en prestaties:

  • Patroon komt overeen met LIKE, VERGELIJKBAAR MET of reguliere expressies in PostgreSQL

pg_trgm biedt ook handige operatoren voor "overeenkomst" (% ) en "afstand" (<-> ).

Trigram-indexen ondersteunen ook eenvoudige reguliere expressies met ~ et al. en hoofdlettergevoelig patroonovereenkomst met ILIKE :

  • PostgreSQL-accent + hoofdletterongevoelig zoeken


  1. Voeg twee tabellen samen in één SQL-query en maak de datumwaarden uniek

  2. Opgeslagen SQL-aanroepprocedure voor elke rij zonder een cursor te gebruiken

  3. java.sql.SQLException:geen geschikte driver gevonden voor jdbc:mysql://localhost:3306/dbname

  4. Hoe COUNT(*) prestaties op InnoDB te optimaliseren met behulp van index