sql >> Base de Datos >  >> RDS >> PostgreSQL

¿Admite PostgreSQL colaciones insensibles al acento?

Usa el módulo sin acento para eso, que es completamente diferente de lo que está vinculando.

unccent es un diccionario de búsqueda de texto que elimina los acentos (signos diacríticos) de los lexemas.

Instalar una vez por base de datos con:

CREATE EXTENSION unaccent;

Si recibe un error como:

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

Instale el paquete contrib en su servidor de base de datos como se indica en esta respuesta relacionada:

  • Error al crear una extensión sin acento en PostgreSQL

Entre otras cosas, proporciona la función unaccent() puede usar con su ejemplo (donde LIKE parece no ser necesario).

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

Índice

Para usar un índice para ese tipo de consulta, cree un índice en la expresión. Sin embargo , Postgres solo acepta IMMUTABLE Funciones para índices. Si una función puede devolver un resultado diferente para la misma entrada, el índice podría romperse silenciosamente.

unaccent() solo STABLE no IMMUTABLE

Desafortunadamente, unaccent() es solo STABLE , no IMMUTABLE . De acuerdo con este hilo sobre pgsql-bugs, esto se debe a tres razones:

  1. Depende del comportamiento de un diccionario.
  2. No hay conexión por cable a este diccionario.
  3. Por lo tanto, también depende de la search_path actual , que puede cambiar fácilmente.

Algunos tutoriales en la web dan instrucciones para modificar la volatilidad de la función a IMMUTABLE . Este método de fuerza bruta puede romperse bajo ciertas condiciones.

Otros sugieren un simple IMMUTABLE función contenedora (como lo hice yo mismo en el pasado).

Hay un debate en curso sobre si hacer la variante con dos parámetros IMMUTABLE que declara explícitamente el diccionario usado. Leer aquí o aquí.

Otra alternativa sería este módulo con un IMMUTABLE unaccent() función de Musicbrainz, proporcionada en Github. No lo he probado yo mismo. Creo que se me ha ocurrido una idea mejor :

Mejor por ahora

Este enfoque es más eficiente que otras soluciones disponibles y más seguro .
Crear un IMMUTABLE Función contenedora de SQL que ejecuta el formulario de dos parámetros con una función y un diccionario cableados y calificados para el esquema.

Dado que anidar una función no inmutable deshabilitaría la función en línea, basarla en una copia de la función C, (falsa) declarada IMMUTABLE así como. Es solo El propósito es ser utilizado en el envoltorio de la función SQL. No está destinado a ser utilizado por sí solo.

La sofisticación es necesaria ya que no hay forma de conectar el diccionario en la declaración de la función C. (Requeriría piratear el código C en sí). La función contenedora de SQL hace eso y permite que ambas funciones alineen y índices de expresión.

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

Soltar PARALLEL SAFE de ambas funciones para Postgres 9.5 o anterior.

public siendo el esquema donde instaló la extensión (public es el predeterminado).

La declaración de tipo explícita (regdictionary ) defiende contra hipotéticos ataques con variantes sobrecargadas de la función por parte de usuarios malintencionados.

Anteriormente, abogué por una función contenedora basada en STABLE función unaccent() enviado con el módulo sin acento. Esa función deshabilitada en línea. Esta versión se ejecuta diez veces más rápido que la función contenedora simple que tenía aquí antes.
Y eso ya era el doble de rápido que la primera versión que agregaba SET search_path = public, pg_temp a la función, hasta que descubrí que el diccionario también puede ser calificado por esquema. Todavía (Postgres 12) no es demasiado obvio a partir de la documentación.

Si carece de los privilegios necesarios para crear funciones C, vuelve a la segunda mejor implementación:un IMMUTABLE contenedor de función alrededor de STABLE unaccent() función proporcionada por el módulo:

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;

Finalmente, el índice de expresión para hacer consultas rápidas :

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

Recuerde recrear índices involucrando esta función después de cualquier cambio en la función o el diccionario, como una actualización de versión principal en el lugar que no recrearía índices. Los principales lanzamientos recientes tenían actualizaciones para unaccent módulo.

Adapte las consultas para que coincidan con el índice (para que el planificador de consultas lo use):

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

No necesita la función en la expresión correcta. Allí también puede proporcionar cadenas sin acento como 'Joao' directamente.

La función más rápida no se traduce en consultas mucho más rápidas utilizando el índice de expresión . Eso opera con valores precalculados y ya es muy rápido. Pero el mantenimiento del índice y las consultas no utilizan el beneficio del índice.

La seguridad de los programas cliente se ha reforzado con Postgres 10.3/9.6.8, etc. Usted necesita para calificar la función de esquema y el nombre del diccionario como se demuestra cuando se usa en cualquier índice. Ver:

  • Las entradas del diccionario de búsqueda de texto "sin acento" no existen en el registro de postgres, supuestamente durante el análisis automático

Ligaduras

En Postgres 9.5 o anterior ligaduras como 'Œ' o 'ß' deben expandirse manualmente (si es necesario), ya que unaccent() siempre sustituye un single letra:

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

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

Te encantará esta actualización a unccent en Postgres 9.6 :

Extender contrib/unaccent unaccent.rules estándar de 's archivo para manejar todos los signos diacríticos conocidos en Unicode, y expandir las ligaduras correctamente (Thomas Munro, Léonard Benedetti)

Énfasis en negrita mío. Ahora obtenemos:

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

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

Coincidencia de patrones

Para LIKE o LIKE con patrones arbitrarios, combine esto con el módulo pg_trgm en PostgreSQL 9.1 o posterior. Cree un trigrama GIN (generalmente preferible) o un índice de expresión GIST. Ejemplo de GIN:

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

Se puede utilizar para consultas como:

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

Los índices GIN y GIST son más caros de mantener que el simple btree:

  • Diferencia entre el índice GiST y GIN

Hay soluciones más simples para patrones anclados a la izquierda. Más información sobre coincidencia de patrones y rendimiento:

  • Coincidencia de patrones con LIKE, SIMILAR TO o expresiones regulares en PostgreSQL

pg_trgm también proporciona operadores útiles para "similitud" (% ) y "distancia" (<-> ).

Los índices de trigramas también admiten expresiones regulares simples con ~ et al. y sin distinción entre mayúsculas y minúsculas coincidencia de patrones con ILIKE :

  • Acento de PostgreSQL + búsqueda que no distingue entre mayúsculas y minúsculas