Como posso fazer uma comparação de seqüências sensíveis a maiúsculas e minúsculas no MySQL?


285

Eu tenho uma função que retorna cinco caracteres com maiúsculas e minúsculas. Se eu fizer uma consulta nessa string, ele retornará o valor, independentemente do caso.

Como posso fazer com que as consultas de string do MySQL sejam sensíveis a maiúsculas e minúsculas?



8
Observe que BINARY não é o mesmo que comparação com distinção entre maiúsculas e minúsculas: selecione 'à' como 'a' // retorna verdadeiro selecione 'à' como BINARY 'a' // retorna falso !!! selecione 'à' como 'a' COLLATE latin1_general_cs // retorna true Portanto, a sugestão de usar BINARY para comparação com distinção entre maiúsculas e minúsculas está incorreta.
Cquezel #

3
@ cquezel: Então, você está dizendo que [selecione 'à' como BINARY 'a'] deve retornar true? De qualquer forma, o que isso tem a ver com comparações que diferenciam maiúsculas de minúsculas?
Francisco Zarabozo 31/03

3
@FranciscoZarabozo algumas pessoas abaixo sugeriram o uso da comparação BINARY para fazer uma comparação sensível a maiúsculas e minúsculas. Estou apenas apontando que em outros idiomas, isso provavelmente não funcionará como esperado, pois BINARY não é o mesmo que diferencia maiúsculas de minúsculas.
Cquezel

3
@ cquezel Eu acho que 'à' é uma letra diferente de 'a'. Portanto, a comparação entre os dois deve ser realmente falsa, seja qual for o caso.
Stephane

Respostas:


159

http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html

O conjunto de caracteres e o agrupamento padrão são latin1 e latin1_swedish_ci, portanto, as comparações de cadeias não binárias diferenciam maiúsculas de minúsculas por padrão. Isso significa que, se você pesquisar com o nome da coluna LIKE 'a%', obterá todos os valores da coluna que começam com A ou a. Para tornar essa pesquisa sensível a maiúsculas e minúsculas, verifique se um dos operandos possui um agrupamento sensível a maiúsculas ou minúsculas. Por exemplo, se você estiver comparando uma coluna e uma sequência que possuem o conjunto de caracteres latin1, poderá usar o operador COLLATE para fazer com que o operando tenha o agrupamento latin1_general_cs ou latin1_bin:

col_name COLLATE latin1_general_cs LIKE 'a%'
col_name LIKE 'a%' COLLATE latin1_general_cs
col_name COLLATE latin1_bin LIKE 'a%'
col_name LIKE 'a%' COLLATE latin1_bin

Se você deseja que uma coluna sempre seja tratada com distinção entre maiúsculas e minúsculas, declare-a com um agrupamento com distinção entre maiúsculas e minúsculas ou binário.


4
alguma dica sobre como fazer isso no phpmyadmin?
StevenB

4
@StevenB: Clique no botão Editar da coluna, em seguida, definir o agrupamento -> i.imgur.com/7SoEw.png
Drudge

32
@BT para fazer caso coluna utf8 sensível você poderia usar colation bin como:SELECT 'email' COLLATE utf8_bin = 'Email'
piotrekkr

@drudge Como você declararia uma coluna com um agrupamento que diferencia maiúsculas de minúsculas?
Stephane

1
@StephaneEybert se você estiver procurando por uma distinção entre maiúsculas e minúsculas, tive sorte em usar varbinary em vez de varchar para um campo na tabela ut8. HTH
Andrew T

724

A boa notícia é que, se você precisar fazer uma consulta que diferencia maiúsculas de minúsculas, é muito fácil:

SELECT *  FROM `table` WHERE BINARY `column` = 'value'

34
Era exatamente isso que eu estava procurando. Eu aumentaria se pudesse. Uma pergunta, porém, que efeito isso tem no desempenho? Estou usando-o em uma reportagem limitada, portanto não é importante no meu caso, mas estou curioso.
adjwilli

23
Por que essa não é a resposta? Isso é exatamente o que eu precisava também.
Art Geigel

7
@adjwilli Se a coluna fizer parte de um índice, você sofrerá um impacto no desempenho em consultas dependentes desse índice. Para manter o desempenho, você precisa realmente alterar a tabela.
Dshin 19/09/2013

6
O que isso fará com as seqüências de caracteres UTF-8 que contêm o mesmo caractere com uma representação diferente, por exemplo, usando um caractere de combinação para adicionar um trema? Essas cadeias UTF-8 podem ser tratadas como iguais: convert(char(0x65,0xcc,0x88) using utf8)(ou seja, ecom ¨adição) e convert(char(0xc3,0xab) using utf8)(ou seja ë), mas a adição as BINARYtornará desiguais.
Mvds

3
Como exemplo de desempenho: minha consulta passa de 3,5ms (insignificante) para 1.570ms (isto é cerca de um segundo e meio), consultando uma tabela com aproximadamente 1,8 milhões de linhas.
Lluís Suñol

64

Resposta postada por Craig White, tem grande penalização de desempenho

SELECT *  FROM `table` WHERE BINARY `column` = 'value'

porque não usa índices. Portanto, você precisa alterar a ordenação da tabela como mencionado aqui https://dev.mysql.com/doc/refman/5.7/en/case-sensitivity.html .

OU

Correção mais fácil, você deve usar um BINÁRIO de valor.

SELECT *  FROM `table` WHERE `column` = BINARY 'value'

Por exemplo.

mysql> EXPLAIN SELECT * FROM temp1 WHERE BINARY col1 = "ABC" AND col2 = "DEF" ;
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | temp1  | ALL  | NULL          | NULL | NULL    | NULL | 190543 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+

VS

mysql> EXPLAIN SELECT * FROM temp1 WHERE col1 = BINARY "ABC" AND col2 = "DEF" ;
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
| id | select_type | table | type  | possible_keys | key           | key_len | ref  | rows | Extra                              |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
|  1 | SIMPLE      | temp1 | range | col1_2e9e898e | col1_2e9e898e | 93      | NULL |    2 | Using index condition; Using where |
+----+-------------+-------+-------+---------------+---------------+---------+------+------+------------------------------------+
enter code here

1 linha no conjunto (0,00 s)


Isso não parece fazer distinção entre maiúsculas e minúsculas no 10.3.22-MariaDB (usando libmysql - 5.6.43)
user10398534

40

Em vez de usar o operador =, convém usar LIKE ou LIKE BINARY

// this returns 1 (true)
select 'A' like 'a'

// this returns 0 (false)
select 'A' like binary 'a'


select * from user where username like binary 'a'

Vai levar 'a' e não 'A' em sua condição


Isso não parece fazer distinção entre maiúsculas e minúsculas no 10.3.22-MariaDB (usando libmysql - 5.6.43)
user10398534

17

Para usar um índice antes de usar o BINARY, você pode fazer algo assim se tiver tabelas grandes.

SELECT
   *
FROM
   (SELECT * FROM `table` WHERE `column` = 'value') as firstresult
WHERE
   BINARY `column` = 'value'

A subconsulta resultaria em um subconjunto que não diferencia maiúsculas de minúsculas, do qual você seleciona a única correspondência que diferencia maiúsculas de minúsculas.


Vale a pena comentar que as opções acima só ajudarão, dependendo dos seus dados - a pesquisa sem distinção entre maiúsculas e minúsculas pode retornar um subconjunto de dados bastante grande.
BrynJ

15

A maneira mais correta de executar uma comparação de cadeias com distinção entre maiúsculas e minúsculas sem alterar o agrupamento da coluna que está sendo consultada é especificar explicitamente um conjunto de caracteres e agrupamento para o valor ao qual a coluna está sendo comparada.

select * from `table` where `column` = convert('value' using utf8mb4) collate utf8mb4_bin;

Por que não usar binary?

O uso do binaryoperador é desaconselhável, pois compara os bytes reais das seqüências codificadas. Se você comparar os bytes reais de duas cadeias codificadas usando o conjunto de caracteres diferentes, duas cadeias que devem ser consideradas iguais podem não ser iguais. Por exemplo, se você possui uma coluna que usa o latin1conjunto de caracteres e seu servidor / sessão é utf8mb4, quando você compara a coluna com uma sequência que contém um acento como 'café', ela não corresponde às linhas que contêm a mesma sequência! Isto porque, em latin1E é codificado como o byte 0xE9, mas em utf8que é dois bytes: 0xC3A9.

Por que usar converttão bem quanto collate?

Os agrupamentos devem corresponder ao conjunto de caracteres. Portanto, se seu servidor ou sessão estiver configurado para usar o latin1conjunto de caracteres, você deve usar, collate latin1_binmas se seu conjunto de caracteres estiver, utf8mb4deverá usar collate utf8mb4_bin. Portanto, a solução mais robusta é sempre converter o valor no conjunto de caracteres mais flexível e usar o agrupamento binário para esse conjunto de caracteres.

Por que aplicar o converte collateao valor e não à coluna?

Quando você aplica qualquer função de transformação a uma coluna antes de fazer uma comparação, impede que o mecanismo de consulta use um índice, se houver algum para a coluna, o que poderia diminuir drasticamente a sua consulta. Portanto, é sempre melhor transformar o valor sempre que possível. Quando uma comparação é realizada entre dois valores de sequência e um deles possui um agrupamento especificado explicitamente, o mecanismo de consulta usará o agrupamento explícito, independentemente de qual valor é aplicado.

Sensibilidade ao acento

É importante observar que o MySql não apenas diferencia maiúsculas de minúsculas para colunas usando um _ciagrupamento (que normalmente é o padrão), mas também não diferencia sotaque . Isso significa isso 'é' = 'e'. O uso de um agrupamento binário (ou o binaryoperador) fará com que as comparações de string sejam sensíveis ao acento e também ao caso.

O que é utf8mb4?

O utf8conjunto de caracteres no MySql é um alias utf8mb3que foi descontinuado nas versões recentes porque não suporta caracteres de 4 bytes (o que é importante para a codificação de strings como 🐈). Se você deseja usar a codificação de caracteres UTF8 com o MySql, deve usar o utf8mb4charset.


8

A seguir, versões do MySQL iguais ou superiores a 5,5.

Adicione ao /etc/mysql/my.cnf

  [mysqld]
  ...
  character-set-server=utf8
  collation-server=utf8_bin
  ...

Todos os outros agrupamentos que tentei pareciam fazer distinção entre maiúsculas e minúsculas, apenas "utf8_bin" funcionava.

Não esqueça de reiniciar o mysql depois disso:

   sudo service mysql restart

De acordo com http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html , também há um "latin1_bin".

O "utf8_general_cs" não foi aceito pela inicialização do mysql. (Li "_cs" como "diferencia maiúsculas de minúsculas" - ???).


7

Você pode usar BINARY para diferenciar maiúsculas de minúsculas assim

select * from tb_app where BINARY android_package='com.Mtime';

infelizmente esse sql não pode usar o índice, você sofrerá um impacto no desempenho em consultas dependentes desse índice

mysql> explain select * from tb_app where BINARY android_package='com.Mtime';
+----+-------------+--------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | tb_app | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 1590351 |   100.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+---------+----------+-------------+

Felizmente, tenho alguns truques para resolver este problema

mysql> explain select * from tb_app where android_package='com.Mtime' and BINARY android_package='com.Mtime';
+----+-------------+--------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table  | partitions | type | possible_keys             | key                       | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+--------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | tb_app | NULL       | ref  | idx_android_pkg           | idx_android_pkg           | 771     | const |    1 |   100.00 | Using index condition |
+----+-------------+--------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-----------------------+  

Isso não parece fazer distinção entre maiúsculas e minúsculas no 10.3.22-MariaDB (usando libmysql - 5.6.43)
user10398534

2

Excelente!

Eu compartilho com você o código de uma função que compara senhas:

SET pSignal =
(SELECT DECODE(r.usignal,'YOURSTRINGKEY') FROM rsw_uds r WHERE r.uname =
in_usdname AND r.uvige = 1);

SET pSuccess =(SELECT in_usdsignal LIKE BINARY pSignal);

IF pSuccess = 1 THEN
      /*Your code if match*/
ELSE
      /*Your code if don't match*/

END IF;

Precisa adicionar declare pSuccess BINARY;no início
adinas

2

Não há necessidade de alterar nada no nível do banco de dados, apenas as alterações no SQL Query que funcionarão.

Exemplo -

"SELECT * FROM <TABLE> where userId = '" + iv_userId + "' AND password = BINARY '" + iv_password + "'";

A palavra-chave binária fará distinção entre maiúsculas e minúsculas.


1

O mysql não diferencia maiúsculas de minúsculas por padrão, tente alterar o agrupamento de idiomas para latin1_general_cs

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.