PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

O PostgreSQL suporta agrupamentos insensíveis ao acento?


Use o módulo sem acento para isso - o que é completamente diferente do que você está vinculando.

unaccent é um dicionário de busca de texto que remove acentos (sinais diacríticos) de lexemas.

Instale uma vez por banco de dados com:
CREATE EXTENSION unaccent;

Se você receber um erro como:
ERROR: could not open extension control file
"/usr/share/postgresql/<version>/extension/unaccent.control": No such file or directory

Instale o pacote contrib em seu servidor de banco de dados conforme instruído nesta resposta relacionada:
  • Erro ao criar extensão unaccent no PostgreSQL

Entre outras coisas, ele fornece a função unaccent() você pode usar com seu exemplo (onde LIKE parece não ser necessário).
SELECT *
FROM   users
WHERE  unaccent(name) = unaccent('João');

Índice


Para usar um índice para esse tipo de consulta, crie um índice na expressão. No entanto , Postgres só aceita IMMUTABLE funções para índices. Se uma função puder retornar um resultado diferente para a mesma entrada, o índice poderá ser interrompido silenciosamente.

unaccent() apenas STABLE não IMMUTABLE


Infelizmente, unaccent() é apenas STABLE , não IMMUTABLE . De acordo com este tópico no pgsql-bugs, isso se deve a três razões:
  1. Depende do comportamento de um dicionário.
  2. Não há conexão física com este dicionário.
  3. Portanto, também depende do search_path atual , que pode mudar facilmente.

Alguns tutoriais na web instruem apenas alterar a volatilidade da função para IMMUTABLE . Este método de força bruta pode quebrar sob certas condições.

Outros sugerem um simples IMMUTABLE função wrapper (como eu fiz no passado).

Há um debate em andamento se deve fazer a variante com dois parâmetros IMMUTABLE que declara o dicionário usado explicitamente. Leia aqui ou aqui.

Outra alternativa seria este módulo com um IMMUTABLE unaccent() função do Musicbrainz, fornecida no Github. Eu mesmo não testei. Acho que tive uma ideia melhor :

Melhor por enquanto


Essa abordagem é mais eficiente que outras soluções disponíveis e mais segura .
Crie um IMMUTABLE Função wrapper SQL executando o formulário de dois parâmetros com função e dicionário qualificados pelo esquema com fio.

Como aninhar uma função não imutável desabilitaria o inlining da função, baseie-a em uma cópia da função C, (falsa) declarada IMMUTABLE também. É apenas O objetivo é ser usado no wrapper da função SQL. Não é para ser usado sozinho.

A sofisticação é necessária, pois não há como conectar o dicionário na declaração da função C. (Seria necessário hackear o próprio código C.) A função wrapper SQL faz isso e permite a função inlining e índices de expressão.
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$;

Solte PARALLEL SAFE de ambas as funções para Postgres 9.5 ou anterior.

public sendo o esquema onde você instalou a extensão (public é o padrão).

A declaração de tipo explícita (regdictionary ) defende contra ataques hipotéticos com variantes sobrecarregadas da função por usuários mal-intencionados.

Anteriormente, defendia uma função wrapper baseada no STABLE função unaccent() enviado com o módulo unaccent. Essa função inlining desativada. Esta versão executa dez vezes mais rápido do que a função wrapper simples que eu tinha aqui antes.
E isso já era duas vezes mais rápido que a primeira versão que adicionou SET search_path = public, pg_temp para a função - até que descobri que o dicionário também pode ser qualificado pelo esquema. Ainda (Postgres 12) não é muito óbvio na documentação.


Se você não tem os privilégios necessários para criar funções C, você está de volta à segunda melhor implementação:Um IMMUTABLE wrapper de função em torno do STABLE unaccent() função fornecida pelo 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;

Por fim, o índice de expressão para fazer consultas rápidas :
CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));

Lembre-se de recriar índices envolvendo essa função após qualquer alteração na função ou dicionário, como uma atualização de versão principal no local que não recriaria índices. Os principais lançamentos recentes tiveram atualizações para o unaccent módulo.

Adapte as consultas para corresponder ao índice (para que o planejador de consultas o use):
SELECT * FROM users
WHERE  f_unaccent(name) = f_unaccent('João');

Você não precisa da função na expressão correta. Lá você também pode fornecer strings não acentuadas como 'Joao' diretamente.

A função mais rápida não se traduz em consultas muito mais rápidas usando o índice de expressão . Isso opera em valores pré-calculados e já é muito rápido. Mas a manutenção do índice e as consultas que não usam o benefício do índice.

A segurança para programas clientes foi reforçada com o Postgres 10.3 / 9.6.8 etc. Você precisa para qualificar o esquema e o nome do dicionário conforme demonstrado quando usado em qualquer índice. Ver:
  • 'dicionário de pesquisa de texto "unaccent" não existe' entradas no log do postgres, supostamente durante a análise automática

Ligaduras


No Postgres 9.5 ou mais antigo ligaduras como 'Œ' ou 'ß' precisam ser expandidas manualmente (se você precisar), pois unaccent() sempre substitui um único carta:
SELECT unaccent('Œ Æ œ æ ß');

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

Você vai adorar esta atualização para unaccent no Postgres 9.6 :

Estender contrib/unaccent padrão do unaccent.rules para lidar com todos os sinais diacríticos conhecidos do Unicode e expandir as ligaduras corretamente (Thomas Munro, Léonard Benedetti)

Minha ênfase em negrito. Agora obtemos:
SELECT unaccent('Œ Æ œ æ ß');

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

Correspondência de padrões


Para LIKE ou ILIKE com padrões arbitrários, combine isso com o módulo pg_trgm no PostgreSQL 9.1 ou posterior. Crie um trigrama GIN (normalmente preferível) ou índice de expressão GIST. Exemplo para GIN:
CREATE INDEX users_unaccent_name_trgm_idx ON users
USING gin (f_unaccent(name) gin_trgm_ops);

Pode ser usado para consultas como:
SELECT * FROM users
WHERE  f_unaccent(name) LIKE ('%' || f_unaccent('João') || '%');

Índices GIN e GIST são mais caros de manter do que btree simples:
  • Diferença entre o índice GiST e GIN

Existem soluções mais simples para padrões apenas ancorados à esquerda. Mais sobre correspondência de padrões e desempenho:
  • Correspondência de padrões com LIKE, SIMILAR TO ou expressões regulares no PostgreSQL

pg_trgm também fornece operadores úteis para "semelhança" (% ) e "distância" (<-> ).

Índices Trigram também suportam expressões regulares simples com ~ et ai. e não diferencia maiúsculas de minúsculas correspondência de padrões com ILIKE :
  • Acento PostgreSQL + pesquisa sem distinção entre maiúsculas e minúsculas