O servidor SQL ignora maiúsculas e minúsculas em uma expressão where

91

Como faço para construir uma consulta SQL (MS SQL Server) em que a cláusula "where" não diferencia maiúsculas de minúsculas?

SELECT * FROM myTable WHERE myField = 'sOmeVal'

Eu quero que os resultados voltem ignorando o caso

Raul Agrait
fonte

Respostas:

137

Na configuração padrão de um banco de dados SQL Server, comparações de string são case-insensitive. Se o seu banco de dados substituir essa configuração (por meio do uso de um agrupamento alternativo), você precisará especificar que tipo de agrupamento usar em sua consulta.

SELECT * FROM myTable WHERE myField = 'sOmeVal' COLLATE SQL_Latin1_General_CP1_CI_AS

Observe que o agrupamento que forneci é apenas um exemplo (embora seja mais do que provável que funcione bem para você). Um esboço mais completo dos agrupamentos do SQL Server pode ser encontrado aqui .

Adam Robinson
fonte
Só para confirmar, só precisa ser adicionado uma vez, no final do WHEREenunciado, e afetará todas as WHEREcláusulas, correto?
ashleedawg
Gostaria de saber se sua resposta apresenta algum problema de desempenho ao converter um valor de coluna para UPPERou LOWERmaiúsculas e minúsculas e usar o LIKEpara pesquisar?
Shaiju T
1
@ashleedawg - boa pergunta .. parece ser uma configuração por linha.
Leo Gurdian
30

Normalmente, as comparações de strings não diferenciam maiúsculas de minúsculas. Se seu banco de dados estiver configurado para agrupamento que diferencia maiúsculas de minúsculas, você precisa forçar o uso de um que não diferencia maiúsculas de minúsculas:

SELECT balance FROM people WHERE email = '[email protected]'
  COLLATE SQL_Latin1_General_CP1_CI_AS 
Andrejs Cainikovs
fonte
@AskeB. e Andrejs: Este não é tecnicamente um problema de configuração do banco de dados. Por favor, veja minha resposta para esclarecimentos sobre comparações de strings.
Solomon Rutzky
21

Encontrei outra solução em outro lugar; isto é, usar

upper(@yourString)

mas todo mundo aqui está dizendo que, no SQL Server, não importa porque está ignorando maiúsculas de qualquer maneira? Tenho certeza que nosso banco de dados diferencia maiúsculas de minúsculas.

Danny
fonte
7
Você está certo de que um banco de dados pode ser sensível a maiúsculas e minúsculas, mas isso é bastante ineficiente, mesmo se necessário. COLLATE é a palavra-chave a ser usada.
mjaggard
1
Obrigado por trazer isso à tona, @mjaggard. Espero que você, ou qualquer pessoa que pareça rejeitar minha resposta, elabore para o bem de alguém como eu, que procura e encontra respostas como a minha.
Danny
1
Votei positivamente nisso, pois é uma explicação perfeitamente racional. Agrupe cheiros de muito overhead e se sua string contiver caracteres que o agrupamento não entende? Latim 1 é um péssimo esquema de codificação. Boa sorte para obter resultados significativos se sua string contiver um apóstrofo (como: O'Brien).
Eggmatters de
2
Também votado positivamente. Posso pensar em muitos casos em que isso seria útil. Além disso, geralmente há mais de uma boa maneira de fazer algo.
Inversus
1
Alterar o caso da string para fins de comparação geralmente é ruim. Em alguns idiomas, as conversões de caso não são de ida e volta. isto é, LOWER (x)! = LOWER (UPPER (x)).
Ceisc de
17

As 2 principais respostas (de Adam Robinson e Andrejs Cainikovs ) são meio, meio corretas, no sentido de que funcionam tecnicamente, mas suas explicações estão erradas e, portanto, podem ser enganosas em muitos casos. Por exemplo, embora o SQL_Latin1_General_CP1_CI_ASagrupamento funcione em muitos casos, ele não deve ser considerado o agrupamento adequado, sem distinção entre maiúsculas e minúsculas. Na verdade, dado que o OP está trabalhando em um banco de dados com um agrupamento que diferencia maiúsculas de minúsculas (ou possivelmente binário), sabemos que o OP não está usando o agrupamento que é o padrão para muitas instalações (especialmente qualquer instalado em um sistema operacional usando US Inglês como língua): SQL_Latin1_General_CP1_CI_AS. Claro, o OP pode estar usando SQL_Latin1_General_CP1_CS_AS, mas ao trabalhar comVARCHARdados, é importante não alterar a página de código, pois isso pode levar à perda de dados e isso é controlado pela localidade / cultura do agrupamento (ou seja, Latin1_General vs francês vs hebraico etc.). Por favor, veja o ponto # 9 abaixo.

As outras quatro respostas estão erradas em vários graus.

Esclarecerei todos os mal-entendidos aqui para que os leitores possam fazer as escolhas mais apropriadas / eficientes.

  1. Não use UPPER(). Isso é um trabalho extra completamente desnecessário. Use uma COLLATEcláusula. Uma comparação de string precisa ser feita em ambos os casos, mas o uso UPPER()também tem que verificar, caractere por caractere, para ver se há um mapeamento em maiúsculas e, em seguida, alterá-lo. E você precisa fazer isso dos dois lados. Adicionar COLLATEsimplesmente direciona o processamento para gerar as chaves de classificação usando um conjunto de regras diferente daquele usado por padrão. Usar COLLATEé definitivamente mais eficiente (ou "performante", se você gosta dessa palavra :) do que usar UPPER(), como comprovado neste script de teste (em PasteBin) .

    Há também o problema observado por @Ceisc na resposta de @ Danny:

    Em alguns idiomas, as conversões de caso não são de ida e volta. ou seja, LOWER (x)! = LOWER (UPPER (x)).

    A maiúscula turca "İ" é o exemplo comum.

  2. Não, o agrupamento não é uma configuração de todo o banco de dados, pelo menos não neste contexto. Há um agrupamento padrão no nível do banco de dados e é usado como padrão para colunas alteradas e criadas recentemente que não especificam a COLLATEcláusula (que é provavelmente de onde vem esse equívoco comum), mas não afeta as consultas diretamente, a menos que você comparando literais e variáveis ​​de string com outros literais e variáveis ​​de string, ou você está fazendo referência a metadados no nível do banco de dados.

  3. Não, o agrupamento não é por consulta.

  4. Os agrupamentos são por predicado (ou seja, algo operando algo) ou expressão, não por consulta. E isso é verdade para toda a consulta, não apenas para a WHEREcláusula. Isso cobre JOINs, GROUP BY, ORDER BY, PARTITION BY, etc.

  5. Não, não converta para VARBINARY(por exemplo convert(varbinary, myField) = convert(varbinary, 'sOmeVal')) pelos seguintes motivos:

    1. essa é uma comparação binária, que não faz distinção entre maiúsculas e minúsculas (que é o que esta pergunta está pedindo)
    2. se você quiser uma comparação binária, use um agrupamento binário. Use um que termine com _BIN2se estiver usando o SQL Server 2008 ou mais recente; caso contrário, você não terá escolha a não ser usar um que termine com _BIN. Se os dados forem NVARCHAR, não importa qual local você usar, pois todos são iguais nesse caso, portanto, Latin1_General_100_BIN2sempre funciona. Se os dados forem VARCHAR, você deve usar o mesmo local que os dados estão atualmente em (por exemplo Latin1_General, French,Japanese_XJIS , etc), porque o local determina a página de código que é usado, e mudar as páginas de código pode alterar os dados (perda de dados IE).
    3. usar um tipo de dados de comprimento variável sem especificar o tamanho dependerá do tamanho padrão, e há dois padrões diferentes dependendo do contexto em que o tipo de dados está sendo usado. É 1 ou 30 para tipos de string. Quando usado com CONVERT()ele, usará o valor padrão 30. O perigo é que, se a string pode ter mais de 30 bytes, ela ficará silenciosamente truncada e você provavelmente obterá resultados incorretos com esse predicado.
    4. Mesmo se você quiser uma comparação com distinção entre maiúsculas e minúsculas, os agrupamentos binários não diferenciam maiúsculas de minúsculas (outro equívoco muito comum).
  6. Não, LIKEnem sempre diferencia maiúsculas de minúsculas. Ele usa o agrupamento da coluna que está sendo referenciada, ou o agrupamento do banco de dados se uma variável for comparada a um literal de string ou o agrupamento especificado por meio da COLLATEcláusula opcional .

  7. LCASEnão é uma função do SQL Server. Parece ser Oracle ou MySQL. Ou possivelmente Visual Basic?

  8. Uma vez que o contexto da questão está comparando uma coluna a um literal de string, nem o agrupamento da instância (muitas vezes referido como "servidor") nem o agrupamento do banco de dados têm qualquer impacto direto aqui. Os agrupamentos são armazenados para cada coluna e cada coluna pode ter um agrupamento diferente, e esses agrupamentos não precisam ser iguais ao agrupamento padrão do banco de dados ou ao agrupamento da instância. Claro, o agrupamento de instância é o padrão para o que um banco de dados recém-criado usará como seu agrupamento padrão se a COLLATEcláusula não foi especificada ao criar o banco de dados. E da mesma forma, o agrupamento padrão do banco de dados é o que uma coluna alterada ou recém-criada usará se a COLLATEcláusula não for especificada.

  9. Você deve usar o agrupamento que não diferencia maiúsculas de minúsculas que, de outra forma, é o mesmo que o agrupamento da coluna. Use a seguinte consulta para encontrar o agrupamento da coluna (altere o nome da tabela e o nome do esquema):

    SELECT col.*
    FROM   sys.columns col
    WHERE  col.[object_id] = OBJECT_ID(N'dbo.TableName')
    AND    col.[collation_name] IS NOT NULL;
    

    Em seguida, basta alterar o _CSpara ser _CI. Então, Latin1_General_100_CS_ASse tornaria Latin1_General_100_CI_AS.

    Se a coluna estiver usando um agrupamento binário (terminando em _BINou _BIN2), encontre um agrupamento semelhante usando a seguinte consulta:

    SELECT *
    FROM   sys.fn_helpcollations() col
    WHERE  col.[name] LIKE N'{CurrentCollationMinus"_BIN"}[_]CI[_]%';
    

    Por exemplo, supondo que a coluna esteja usando Japanese_XJIS_100_BIN2, faça o seguinte:

    SELECT *
    FROM   sys.fn_helpcollations() col
    WHERE  col.[name] LIKE N'Japanese_XJIS_100[_]CI[_]%';
    

Para mais informações sobre agrupamentos, codificações, etc, visite: Collations Informações

Solomon Rutzky
fonte
7

Não, apenas o uso LIKEnão funcionará. LIKEprocura valores que correspondam exatamente ao seu padrão fornecido. Neste caso LIKE, encontraria apenas o texto 'sOmeVal' e não 'someval'.

Uma solução prática é usar a LCASE()função. LCASE('sOmeVal')obtém a string minúscula do seu texto: 'someval'. Se você usar esta função para ambos os lados da comparação, ela funcionará:

SELECT * FROM myTable WHERE LCASE(myField) LIKE LCASE('sOmeVal')

A instrução compara duas strings em minúsculas, de modo que seu 'sOmeVal' corresponderá a todas as outras notações de 'someval' (por exemplo, 'Someval', 'sOMEVAl' etc.).

David Hermanns
fonte
7
Em 99,9% das instalações do SQL Server que são agrupadas _CI, LIKE não faz distinção entre maiúsculas e minúsculas.
RichardTheKiwi
1
Hoje em dia a função chama-se LOWER
David Brossard
@DavidBrossard e David Hermanns, acho que nunca foi LCASE()no SQL Server (pelo menos não que eu consiga ver). Acho que essa resposta é para um RDBMS totalmente diferente. Por favor, veja minha resposta para esclarecimentos sobre comparações de strings.
Solomon Rutzky
4

Você pode forçar a distinção entre maiúsculas e minúsculas, convertendo para um varbinary como este:

SELECT * FROM myTable 
WHERE convert(varbinary, myField) = convert(varbinary, 'sOmeVal')

fonte
3
Embora seja funcional, não é uma abordagem aconselhável. Os agrupamentos existem para gerenciar a classificação e as comparações de strings.
Adam Robinson
@AdamRobinson não é sobre "comparações de strings"?
Fandango68
@ Fandango68 Sim, é, e Adam está dizendo que agrupamentos são melhores ao fazer comparações de strings.
JLRishe
@ Fandango68 Essa resposta está errada em vários níveis. Por favor, veja minha resposta para detalhes, especialmente ponto 5.
Solomon Rutzky
@AdamRobinson Por favor, veja minha resposta para esclarecimentos sobre comparações de strings.
Solomon Rutzky
2

Em qual banco de dados você está? Com o MS SQL Server, é uma configuração para todo o banco de dados, ou você pode substituí-la por consulta com a palavra-chave COLLATE.

Chase Seibert
fonte
Olá. Para o SQL Server, em termos do que trata esta questão, não é uma configuração de todo o banco de dados nem por consulta. Por favor, veja minha resposta para detalhes.
Solomon Rutzky