¿PostgreSQL admite intercalaciones "insensibles al acento"?

En Microsoft SQL Server, es posible especificar una intercalación "insensible a los acentos" (para una base de datos, tabla o columna), lo que significa que es posible para una consulta como

SELECT * FROM users WHERE name LIKE 'João'

para encontrar una fila con un Joao nombre.

Sé que es posible quitar acentos de cadenas en PostgreSQL usando el cadena_sinaccent contrib, pero me pregunto si PostgreSQL admite estas intercalaciones "insensibles al acento", por lo que SELECT arriba funcionaría.

preguntado el 12 de junio de 12 a las 22:06

Vea esta respuesta para crear un diccionario FTS sin acento: stackoverflow.com/a/50595181/124486 -

¿Quieres búsquedas que distingan entre mayúsculas y minúsculas o que no distingan entre mayúsculas y minúsculas? -

3 Respuestas

Use 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:

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');

Home

Para usar un índice para ese tipo de consulta, cree un índice de 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() , solamente STABLE no IMMUTABLE

Desafortunadamente, unaccent() es sólo STABLEno, IMMUTABLE. De acuerdo a este hilo en pgsql-bugs, esto es debido a Tres razones:

  1. Depende del comportamiento de un diccionario.
  2. No hay una conexión por cable a este diccionario.
  3. Por lo tanto, también depende de la corriente search_path, 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 sencillo IMMUTABLE función de envoltura (como lo hice yo mismo en el pasado).

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

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

mejor por ahora

Este enfoque es más eficiente que otras soluciones que flotan alrededor, y más seguro.
Crear una 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 también. Sus , solamente 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 se 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 instalaste 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.

Previamente, abogué por una función contenedora basada en el STABLE función unaccent() enviado con el módulo sin acento. que discapacitado función en línea. Esta versión 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 agregó SET search_path = public, pg_temp a la función, hasta que descubrí que el diccionario también puede ser calificado por esquema. Aún así (Postgres 12) no es demasiado obvio a partir de la documentación.

If carece de los privilegios necesarios para crear funciones C, vuelve a la segunda mejor implementación: una IMMUTABLE contenedor de funciones alrededor de la 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, la índice de expresión para hacer consultas rápido:

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

Recuerda 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 el 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. necesitas para calificar la función de esquema y el nombre del diccionario como se demuestra cuando se usa en cualquier índice. Ver:

Ligaduras

En Postgres Mayor de 9.5 años las ligaduras como 'Œ' o 'ß' deben expandirse manualmente (si lo necesita), ya que unaccent() siempre sustituye a soltero carta:

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

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

Amarás esta actualización a sin acento en postgres 9.6:

ampliar contrib/unaccentestándar de unaccent.rules archivo para manejar todos los signos diacríticos conocidos por Unicode, y expandir ligaduras correctamente (Thomas Munro, Leonard Benedetti)

Énfasis en negrita mío. Ahora obtenemos:

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

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

La coincidencia de patrones

Para LIKE or ILIKE 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:

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

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

Los índices de trigramas también admiten expresiones regulares simples con ~ et al. y insensible a mayúsculas y minúsculas patrón a juego con ILIKE:

Respondido 04 Oct 19, 01:10

En su solución, ¿se utilizan índices o necesitaría crear un índice en unaccent(name)? - daniel serodio

@e3matheus: Sintiéndome culpable por no haber probado la solución anterior que proporcioné, investigué y actualicé mi respuesta con una solución nueva y mejor (en mi humilde opinión) para el problema que la que está flotando hasta ahora. - Erwin Brandstetter

Sus respuestas son tan buenas como la documentación de Postgres: ¡fenomenal! - electrotipia

Me pregunto si ahora es posible no tener en cuenta el acento con las intercalaciones de la UCI. - un caballo sin nombre

@a_horse_with_no_name: Todavía no tuve tiempo de probarlo, pero ese es un caso de uso previsto. - Erwin Brandstetter

No, PostgreSQL no admite intercalaciones en ese sentido

PostgreSQL no admite intercalaciones como esa (insensible al acento o no) porque ninguna comparación puede devolver lo mismo a menos que las cosas sean binarias iguales. Esto se debe a que internamente introduciría muchas complejidades para cosas como un índice hash. Por ello las colaciones en su sentido más estricto solo afecta el pedido y no la igualdad.

Soluciones provisionales

Diccionario de búsqueda de texto completo que desacentúa los lexemas.

Para FTS, puede definir su propio diccionario usando unaccent,

CREATE EXTENSION unaccent;

CREATE TEXT SEARCH CONFIGURATION mydict ( COPY = simple );
ALTER TEXT SEARCH CONFIGURATION mydict
  ALTER MAPPING FOR hword, hword_part, word
  WITH unaccent, simple;

Que luego puede indexar con un índice funcional,

-- Just some sample data...
CREATE TABLE myTable ( myCol )
  AS VALUES ('fóó bar baz'),('qux quz');

-- No index required, but feel free to create one
CREATE INDEX ON myTable
  USING GIST (to_tsvector('mydict', myCol));

Ahora puedes consultarlo de forma muy sencilla.

SELECT *
FROM myTable
WHERE to_tsvector('mydict', myCol) @@ 'foo & bar'

    mycol    
-------------
 fóó bar baz
(1 row)

Vea también

Sin acento por sí mismo.

La unaccent módulo también se puede usar solo sin integración FTS, para eso echa un vistazo La respuesta de Erwin

contestado el 30 de mayo de 18 a las 02:05

Tenga en cuenta que el párrafo inicial aquí ya no es estrictamente cierto a partir de Postgres 12 que introdujo intercalaciones no deterministas. Sin embargo, todavía no son compatibles con los operadores de coincidencia de patrones. - indicio

Estoy bastante seguro de que PostgreSQL se basa en el sistema operativo subyacente para la intercalación. Eso ASISTENCIA creando nuevas intercalacionesy personalización de intercalaciones. Sin embargo, no estoy seguro de cuánto trabajo podría ser para ti. (Podría ser bastante).

Respondido el 12 de junio de 12 a las 22:06

Actualmente, el nuevo soporte de intercalación se limita básicamente a contenedores y alias para las configuraciones regionales del sistema operativo. Es muy básico. No hay soporte para funciones de filtro, comparadores personalizados ni nada de lo que necesitaría para verdaderas intercalaciones personalizadas. - craig timbre

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.