Use o módulo unaccent para isso - que é completamente diferente do que você está vinculando.
unaccent é um dicionário de pesquisa 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:
Entre outras coisas, ele fornece a função que 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 , o Postgres só aceita IMMUTABLE
funções para índices. Se uma função pode retornar um resultado diferente para a mesma entrada, o índice pode quebrar silenciosamente.
unaccent()
só STABLE
nãoIMMUTABLE
Infelizmente, unaccent()
é apenas STABLE
, não IMMUTABLE
. De acordo com este tópico sobre pgsql-bugs , isso se deve a três motivos:
- Depende do comportamento de um dicionário.
- Não há conexão física com este dicionário.
- Portanto, também depende da corrente
search_path
, que pode mudar facilmente.
Alguns tutoriais na web instruem apenas a alterar a volatilidade da função para IMMUTABLE
. Este método de força bruta pode quebrar sob certas condições.
Outros sugerem uma função de invólucro simplesIMMUTABLE
(como eu fazia no passado).
Há um debate em andamento quanto a fazer a variante com dois parâmetros IMMUTABLE
que declara explicitamente o dicionário usado. Leia aqui ou aqui .
Outra alternativa seria este módulo com uma IMUTÁVEL unaccent()
função por Musicbrainz , fornecida no Github. Não testei sozinho. Acho que tive uma ideia melhor :
Melhor por agora
Essa abordagem é mais eficiente do que outras soluções flutuando e mais segura .
Crie uma IMMUTABLE
função de wrapper SQL executando a forma de dois parâmetros com a função qualificada pelo esquema conectado e um dicionário.
Como aninhar uma função não imutável desabilitaria o inlining de função, baseie-o em uma cópia da função C, (falsa) declarada IMMUTABLE
também. Seu único propósito é ser usado no wrapper de função SQL. Não deve 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 SQL wrapper faz isso e permite o inlining de função e os í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$;
Remova as PARALLEL SAFE
duas funções para Postgres 9.5 ou mais antigo.
public
sendo o esquema onde você instalou a extensão ( public
é o padrão).
A declaração de tipo explícita ( regdictionary
) protege contra ataques hipotéticos com variantes sobrecarregadas da função por usuários mal-intencionados.
Anteriormente, defendi uma função de invólucro com base na STABLE
função unaccent()
fornecida com o módulo unaccent. Essa função desativada inlining . Esta versão executa dez vezes mais rápido do que a função de invólucro simples que tive aqui anteriormente.
E isso já foi duas vezes mais rápido do que a primeira versão adicionada SET search_path = public, pg_temp
à função - até que descobri que o dicionário também pode ser qualificado pelo esquema. Ainda (Postgres 12) não muito óbvio pela documentação.
Se você não tiver os privilégios necessários para criar funções C, estará de volta à segunda melhor implementação: um IMMUTABLE
wrapper de função em torno da 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;
Finalmente, o índice de expressão para tornar as consultas rápidas :
CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));
Lembre-se de recriar índices envolvendo esta 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. Todos os lançamentos principais recentes tinham atualizações para o unaccent
módulo.
Adapte as consultas para corresponder ao índice (para que o planejador de consultas as use):
SELECT * FROM users
WHERE f_unaccent(name) = f_unaccent('João');
Você não precisa da função na expressão certa. Lá você também pode fornecer strings sem ênfase '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 não usam o benefício do índice.
A segurança para programas cliente foi reforçada com Postgres 10.3 / 9.6.8 etc. Você precisa qualificar o esquema da função e do nome do dicionário conforme demonstrado quando usado em quaisquer índices. Vejo:
Ligaduras
No Postgres 9.5 ou anteriores, ligaduras como 'Œ' ou 'ß' devem ser expandidas manualmente (se necessário), pois unaccent()
sempre substitui uma única letra:
SELECT unaccent('Œ Æ œ æ ß');
unaccent
----------
E A e a S
Você vai adorar esta atualização para unaccent no Postgres 9.6 :
Estenda contrib/unaccent
o unaccent.rules
arquivo padrão para lidar com todos os diacríticos conhecidos pelo Unicode e expanda as ligaduras corretamente (Thomas Munro, Léonard Benedetti)
Ênfase em negrito minha. Agora temos:
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') || '%');
Os índices GIN e GIST são mais caros de manter do que o btree simples:
Existem soluções mais simples para padrões ancorados apenas à esquerda. Mais sobre correspondência de padrões e desempenho:
pg_trgm
também fornece operadores%
<->
úteis para "similaridade" ( ) e "distância" ( ) .
Os índices trigramas também suportam expressões regulares simples com ~
et al. e o padrão não diferencia maiúsculas de minúsculas combinando com ILIKE
: