PostgreSQL: Como fazer uma consulta que não diferencia maiúsculas de minúsculas

338

Existe alguma maneira de escrever consultas que não diferenciam maiúsculas de minúsculas no PostgreSQL, por exemplo, eu quero que as 3 consultas a seguir retornem o mesmo resultado.

SELECT id FROM groups where name='administrator'

SELECT id FROM groups where name='ADMINISTRATOR'

SELECT id FROM groups where name='Administrator'
Jame
fonte
Se o citext vier com a instalação do Postgres, tente o tipo citext. É um texto que não diferencia maiúsculas de minúsculas
Michael Buen
2
Para os iniciantes nesta questão, este link para a documentação oficial do postgres contém todas as respostas fornecidas aqui, além de algumas outras opções.
Parthian Shot
Sir reatribuir resposta aceita à feita por @Arun, por favor. É muito menos complicado e não gera muitos problemas após a aplicação.
Zeliboba

Respostas:

451

Use a função LOWER para converter as strings em minúsculas antes de comparar.

Tente o seguinte:

SELECT id 
  FROM groups
 WHERE LOWER(name)=LOWER('Administrator')
Chandu
fonte
92
É importante observar que o uso de LOWER (ou qualquer função) nas colunas de predicado - nesse caso, "nome" - fará com que os índices não sejam mais procuráveis. Se esta for uma tabela grande ou frequentemente consultada, isso poderá causar problemas. O agrupamento sem distinção entre maiúsculas e minúsculas, citext ou um índice baseado em função melhorará o desempenho.
Jordânia
108
Ou apenas crie um índice como este: CREATE INDEX idx_groups_name ON groups lower (name);
Daniel
19
Especifique também varchar_pattern_opsse deseja que o índice funcione com a LIKE 'xxx%'consulta, ou seja CREATE INDEX ix_groups_name ON groups (lower(name) varchar_pattern_ops).
sayap
10
Usar o operador ILIKE (como mostrado em outras respostas abaixo) é uma abordagem mais simples, mesmo que essa seja a resposta mais votada.
21715 Ryan
5
Analisando os comentários aqui, muitas sugestões sugerem ILIKE: Funcionará but with slow response. Para obter acesso rápido às tabelas com base nos resultados dos cálculos, sugiro que qualquer pessoa que verifique isso deve ir com a resposta aceita. Veja mais detalhes aqui e aqui
Afolabi Olaoluwa Akinwumi / 01/16
230

usando em ILIKEvez deLIKE

SELECT id FROM groups WHERE name ILIKE 'Administrator'
Mohammad Reza Norouzi
fonte
11
Observe que o ILIKEHibernate não é suportado quando usado no Spring Boot.
AnT
O @AnT funciona com o org.hibernate.dialect.PostgreSQL94DialectSpring Boot 2.0.6.RELEASE. Mas o IntelliJ reclama disso.
Samintha Kaveesh
134

A abordagem mais comum é minúscula ou maiúscula a sequência de pesquisa e os dados. Mas há dois problemas com isso.

  1. Funciona em inglês, mas não em todos os idiomas. (Talvez nem na maioria dos idiomas.) Nem toda letra minúscula tem uma letra maiúscula correspondente; nem toda letra maiúscula possui uma letra minúscula correspondente.
  2. Usar funções como inferior () e superior () fornecerá uma varredura seqüencial. Não pode usar índices. No meu sistema de teste, usar lower () leva cerca de 2000 vezes mais que uma consulta que pode usar um índice. (Os dados de teste têm pouco mais de 100 mil linhas.)

Existem pelo menos três soluções usadas com menos frequência que podem ser mais eficazes.

  1. Use o módulo citext , que imita principalmente o comportamento de um tipo de dados que não diferencia maiúsculas de minúsculas. Depois de carregar esse módulo, você pode criar um índice que não diferencia maiúsculas de minúsculas CREATE INDEX ON groups (name::citext);. (Mas veja abaixo.)
  2. Use um agrupamento que não diferencia maiúsculas de minúsculas. Isso é definido quando você inicializa um banco de dados. Usar um agrupamento sem distinção entre maiúsculas e minúsculas significa que você pode aceitar praticamente qualquer formato do código do cliente e ainda retornará resultados úteis. (Isso também significa que você não pode fazer consultas com distinção entre maiúsculas e minúsculas. Duh.)
  3. Crie um índice funcional. Crie um índice em minúsculas usando CREATE INDEX ON groups (LOWER(name));. Feito isso, você pode tirar proveito do índice com consultas como SELECT id FROM groups WHERE LOWER(name) = LOWER('ADMINISTRATOR');, ou SELECT id FROM groups WHERE LOWER(name) = 'administrator';Você deve se lembrar de usar LOWER ().

O módulo citext não fornece um tipo de dados que não diferencia maiúsculas de minúsculas. Em vez disso, ele se comporta como se cada sequência tivesse letras minúsculas. Ou seja, ele se comporta como se você tivesse chamado lower()cada string, como no número 3 acima. A vantagem é que os programadores não precisam se lembrar de minúsculas. Mas você precisa ler as seções "Comportamento de comparação de strings" e "Limitações" nos documentos antes de decidir usar o citext.

Mike Sherrill 'Recolha de Gatos'
fonte
11
Sobre o número 1: não deve ser um problema, pois seriam duas seqüências diferentes (pense nisso como fazer col = 'a'e col = 'b'). Sobre o item 2: Como você disse, você pode criar um índice em uma expressão, para que não seja realmente um problema. Mas concordo com você que a alteração do agrupamento provavelmente é a melhor solução.
Vincent Savard
5
Alguém pode me dizer quais agrupamentos que não diferenciam maiúsculas de minúsculas são agrupamentos internos do PostgreSQL? Eu vejo isso como uma opção, mas não consigo encontrar nada sobre um agrupamento que não diferencia maiúsculas de minúsculas do Postgres na rede?
khorvat
11
@ AnupShah: Não, não estou dizendo isso. Não estou executando o PostgreSQL no Windows. Os documentos 9.4 dizem o seguinte : "Em todas as plataformas, os agrupamentos nomeados padrão, C e POSIX estão disponíveis. Agrupamentos adicionais podem estar disponíveis, dependendo do suporte do sistema operacional." Você pode ver com quais agrupamentos o PostgreSQL acha que estão disponíveis select * from pg_collation;.
Mike Sherrill 'Recall Cat'
11
@ Matthieu: Esta é a melhor introdução (e cautela) ao assunto que eu conheço: casos de borda a serem lembrados. Parte 1 - Texto .
Mike Sherrill 'Cat Recall'
11
@ Matthieu: O FAQ Unicode também é divertido de ler. Aqui está o Por que não existe um caractere maiúsculo exclusivo. . .
Mike Sherrill 'Cat Recall'
95

Você pode usar ILIKE. ie

SELECT id FROM groups where name ILIKE 'administrator'
ADJ
fonte
Está correto e está funcionando bem para mim, estou usando o MAC OS X (Mountain Lion).
ADJ
5
Isso funcionará, mas com resposta lenta. Para obter acesso rápido às tabelas com base nos resultados dos cálculos, sugiro usar a lowerfunção Veja mais detalhes
Afolabi Olaoluwa Akinwumi / 01/16
11
@AfolabiOlaoluwaAkinwumi basicamente se resume a se você está procurando resultados em vez de filtrar valores conhecidos . Neste último caso, um único caso uniforme deve persistir no nível dos dados, permitindo que o operador de igualdade trabalhe. [Recomendação pessoal é caso pascal superior para valores de código tipo]
Chris Marisic
53

Você também pode ler a ILIKEpalavra - chave. Às vezes, pode ser bastante útil, embora não esteja em conformidade com o padrão SQL. Veja aqui para mais informações: http://www.postgresql.org/docs/9.2/static/functions-matching.html

Priidu Neemre
fonte
9
Algo a observar aqui é a entrada maliciosa do usuário. Se você executar uma consulta como essa email ILIKE 'user-input-email-here', não se esqueça da entrada do usuário. Caso contrário, as pessoas podem inserir caracteres como% que correspondam a qualquer coisa.
Matt De Leon
2
@MattDeLeon Hi. Bem dito. Mas eu só quero perguntar, se eu uso ILIKEe prepared statementsisso me protegerá sql injection?
slevin
Não tenho certeza, suponho que você queira enviar uma string de escape para a instrução preparada.
Matt De Leon
11
"A palavra-chave ILIKE pode ser usada em vez de LIKE para tornar a correspondência sem distinção entre maiúsculas e minúsculas de acordo com o código do idioma ativo. Isso não está no padrão SQL, mas é uma extensão do PostgreSQL." Funciona como um encanto em 9.3 #
Aleksey Deryagin 23/12/2014
11
ILIKE é mais lento que lower(column_name) like %expression%.
Patryk Imosa
28

Você também pode usar expressões regulares POSIX, como

SELECT id FROM groups where name ~* 'administrator'

SELECT 'asd' ~* 'AsD' retorna t

James Brown
fonte
11
Eu tinha o mesmo problema, precisava de pesquisas sem distinção entre maiúsculas e minúsculas no meu banco de dados PostgreSQL. Pensei em transformar a string de entrada do usuário em uma expressão regular. Agora, usar ~ * em vez de = ou LIKE funcionou perfeitamente! Não precisei criar novos índices, colunas ou o que for. Claro, a pesquisa regex é mais lenta que a comparação de bytes diretos, mas não acho que o impacto no desempenho seja muito maior do que ter que lidar com dois conjuntos de dados (um mais baixo ou em maiúscula apenas para pesquisa e depois recuperar o original correspondente dados do outro conjunto). Além disso, isso é mais limpo!
precisa saber é o seguinte
11
Tudo bem, mas como fazer com regexp_matches () por exemplo?
WKT
De acordo com a documentação do postgres: O operador ~~ é equivalente a LIKE e ~~ * corresponde a ILIKE. Também existem operadores! ~~ e! ~~ * que representam NOT LIKE e NOT ILIKE, respectivamente. Todos esses operadores são específicos do PostgreSQL.
sh4 30/08/19
Eu enfrentei um problema quando colchetes são incluídos no texto, não está funcionando. como: "code (LC)"
Oshan Wisumperuma 23/09/19
8

O uso ~*pode melhorar muito o desempenho, com a funcionalidade do INSTR.

SELECT id FROM groups WHERE name ~* 'adm'

retornar linhas com o nome que contém OR é igual a 'adm'.

Robin Goh
fonte
11
Ei, Robin, bem-vindo ao SO. A resposta de James Brown já propôs essa solução. Além disso, sua resposta proposta não utiliza regex de forma alguma.
Rafael