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

PostgreSQL supporta le regole di confronto insensibili all'accento?

Usa il modulo non accento per quello - che è completamente diverso da quello a cui ti stai collegando.

unaccent è un dizionario di ricerca di testo che rimuove gli accenti (segni diacritici) dai lessemi.

Installa una volta per database con:

CREATE EXTENSION unaccent;

Se ricevi un errore del tipo:

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

Installa il pacchetto contrib sul tuo server di database come indicato in questa risposta correlata:

  • Errore durante la creazione dell'estensione senza accento su PostgreSQL

Tra le altre cose, fornisce la funzione unaccent() puoi usare con il tuo esempio (dove LIKE sembra non necessario).

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

Indice

Per utilizzare un indice per quel tipo di query, creare un indice sull'espressione. Tuttavia , Postgres accetta solo IMMUTABLE funzioni per gli indici. Se una funzione può restituire un risultato diverso per lo stesso input, l'indice potrebbe interrompersi silenziosamente.

unaccent() solo STABLE non IMMUTABLE

Sfortunatamente, unaccent() è solo STABLE , non IMMUTABLE . Secondo questo thread su pgsql-bugs, ciò è dovuto a tre motivi:

  1. Dipende dal comportamento di un dizionario.
  2. Non è presente alcuna connessione cablata a questo dizionario.
  3. Dipende quindi anche dal search_path corrente , che può cambiare facilmente.

Alcuni tutorial sul web indicano di modificare semplicemente la volatilità della funzione in IMMUTABLE . Questo metodo di forza bruta può rompersi in determinate condizioni.

Altri suggeriscono un semplice IMMUTABLE funzione wrapper (come ho fatto io stesso in passato).

È in corso un dibattito se realizzare la variante con due parametri IMMUTABLE che dichiara esplicitamente il dizionario utilizzato. Leggi qui o qui.

Un'altra alternativa sarebbe questo modulo con un unaccent() IMMUTABLE funzione di Musicbrainz, fornita su Github. Non l'ho testato da solo. Penso di aver avuto un'idea migliore :

Il migliore per ora

Questo approccio è più efficiente rispetto ad altre soluzioni in circolazione e più sicuro .
Crea un IMMUTABLE Funzione wrapper SQL che esegue il modulo a due parametri con funzione qualificata per schema cablata e dizionario.

Poiché la nidificazione di una funzione non immutabile disabiliterebbe l'inlining della funzione, basarla su una copia della funzione C, dichiarata (falsa) IMMUTABLE anche. È solo scopo deve essere utilizzato nel wrapper della funzione SQL. Non pensato per essere utilizzato da solo.

La sofisticatezza è necessaria in quanto non c'è modo di cablare il dizionario nella dichiarazione della funzione C. (Richiede l'hacking del codice C stesso.) La funzione SQL wrapper lo fa e consente sia l'inlining delle funzioni che indici di espressione.

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

Rilascia PARALLEL SAFE da entrambe le funzioni per Postgres 9.5 o precedenti.

public essendo lo schema in cui hai installato l'estensione (public è l'impostazione predefinita).

La dichiarazione esplicita del tipo (regdictionary ) difende da ipotetici attacchi con varianti sovraccaricate della funzione da parte di utenti malintenzionati.

In precedenza, sostenevo una funzione wrapper basata su STABLE funzione unaccent() fornito con il modulo unaccent. Quella funzione disabilitata inlineing. Questa versione viene eseguita dieci volte più velocemente rispetto alla semplice funzione wrapper che avevo qui prima.
E questo era già due volte più veloce della prima versione che aggiungeva SET search_path = public, pg_temp alla funzione - fino a quando non ho scoperto che anche il dizionario può essere qualificato per schema. Ancora (Postgres 12) non troppo evidente dalla documentazione.

Se ti mancano i privilegi necessari per creare funzioni C, sei tornato alla seconda migliore implementazione:An IMMUTABLE wrapper della funzione attorno a STABLE unaccent() funzione fornita dal modulo:

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;

Infine, l'indice di espressione per effettuare query velocemente :

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

Ricordati di ricreare gli indici che coinvolgono questa funzione dopo qualsiasi modifica alla funzione o al dizionario, ad esempio un aggiornamento della versione principale sul posto che non ricrea gli indici. Tutte le versioni principali recenti avevano aggiornamenti per unaccent modulo.

Adatta le query in modo che corrispondano all'indice (in modo che il pianificatore di query lo utilizzi):

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

Non hai bisogno della funzione nell'espressione corretta. Lì puoi anche fornire stringhe non accentate come 'Joao' direttamente.

La funzione più veloce non si traduce in query molto più veloci utilizzando l'indice di espressione . Funziona su valori precalcolati ed è già molto veloce. Ma la manutenzione dell'indice e le query non utilizzano il vantaggio dell'indice.

La sicurezza per i programmi client è stata rafforzata con Postgres 10.3 / 9.6.8 ecc. è necessario per qualificare lo schema alla funzione e al nome del dizionario come dimostrato quando utilizzato in qualsiasi indice. Vedi:

  • Voci "unaccent" non esiste nel dizionario di ricerca di testo' nel registro di Postgres, presumibilmente durante l'analisi automatica

Legature

In Postgres 9.5 o versioni precedenti legature come 'Œ' o 'ß' devono essere espanse manualmente (se necessario), poiché unaccent() sostituisce sempre un singolo lettera:

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

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

Amerai questo aggiornamento per annullare l'accento in Postgres 9.6 :

Estendi contrib/unaccent unaccent.rules standard di per gestire tutti i segni diacritici noti a Unicode e espandere correttamente le legature (Thomas Munro, Léonard Benedetti)

Enfasi in grassetto mio. Ora otteniamo:

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

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

Corrispondenza del modello

Per LIKE o LIKE con pattern arbitrari, combinalo con il modulo pg_trgm in PostgreSQL 9.1 o successivo. Crea un trigramma GIN (in genere preferibile) o un indice di espressione GIST. Esempio per GIN:

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

Può essere utilizzato per query come:

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

Gli indici GIN e GIST sono più costosi da mantenere rispetto al semplice btree:

  • Differenza tra indice GiST e GIN

Esistono soluzioni più semplici per i modelli ancorati a sinistra. Maggiori informazioni sulla corrispondenza dei modelli e sulle prestazioni:

  • Corrispondenza del modello con LIKE, SIMILAR TO o espressioni regolari in PostgreSQL

pg_trgm fornisce anche utili operatori per la "somiglianza" (% ) e "distanza" (<-> ).

Gli indici Trigram supportano anche semplici espressioni regolari con ~ et al. e senza distinzione tra maiuscole e minuscole corrispondenza del modello con ILIKE :

  • Accento PostgreSQL + ricerca senza distinzione tra maiuscole e minuscole