Как я могу сделать сравнение строк с учетом регистра SQL в MySQL?

285

У меня есть функция, которая возвращает пять символов в смешанном регистре. Если я сделаю запрос по этой строке, он вернет значение независимо от регистра.

Как я могу сделать строковые запросы MySQL чувствительными к регистру?

StevenB
источник
8
Обратите внимание, что BINARY отличается от сравнения с учетом регистра: select 'à' like 'a' // возвращает true выберите 'à' like BINARY 'a' // возвращает false !!! выберите 'à' like 'a' COLLATE latin1_general_cs // возвращает true Так что предложение использовать BINARY для сравнения с учетом регистра неверно.
cquezel
3
@cquezel: Итак, вы говорите, что [select 'à' like BINARY 'a'] должно возвращать true ?? В любом случае, какое это имеет отношение к сравнениям с учетом регистра?
Франсиско Сарабосо
3
@FranciscoZarabozo. Некоторые из нижеприведенных людей предложили использовать сравнение BINARY для сравнения с учетом регистра. Я просто подчеркиваю, что в других языках это, вероятно, не будет работать так, как ожидалось, поскольку BINARY отличается от регистра.
cquezel
3
@cquezel Я думаю, что «а» - это буква, отличная от «а». Таким образом, сравнение между ними действительно должно быть ложным в любом случае.
Стефан

Ответы:

159

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

Набор символов и параметры сортировки по умолчанию - latin1 и latin1_swedish_ci, поэтому при сравнении недвоичных строк по умолчанию регистр не учитывается. Это означает, что если вы ищете с col_name LIKE 'a%', вы получите все значения столбцов, которые начинаются с A или a. Чтобы сделать этот поиск чувствительным к регистру, убедитесь, что один из операндов имеет чувствительность к регистру или двоичное сопоставление. Например, если вы сравниваете столбец и строку, которые имеют набор символов latin1, вы можете использовать оператор COLLATE, чтобы у любого из операндов был параметр сравнения latin1_general_cs или 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

Если вы хотите, чтобы столбец всегда обрабатывался с учетом регистра, объявите его с учетом регистра или двоичным сопоставлением.

выполнять тяжелую работу
источник
4
Любой намек на то, как это сделать в phpmyadmin?
StevenB
4
@StevenB: Нажмите кнопку Изменить столбца, затем установите параметры сортировки -> i.imgur.com/7SoEw.png
Drudge
32
@BT Чтобы сделать столбец utf8 чувствительным к регистру, вы можете использовать сортировку бинов, например:SELECT 'email' COLLATE utf8_bin = 'Email'
piotrekkr
@drudge Как бы вы объявили столбец с сортировкой с учетом регистра?
Стефан
1
@StephaneEybert, если вы ищете прямую чувствительность к регистру, мне посчастливилось использовать varbinary вместо varchar для поля в таблице ut8. HTH
Эндрю Т
726

Хорошей новостью является то, что если вам нужно сделать запрос с учетом регистра, это очень легко сделать:

SELECT *  FROM `table` WHERE BINARY `column` = 'value'
Крейг Уайт
источник
34
Это именно то, что я искал. Я бы поднялся выше, если бы мог. Вопрос, однако, как это влияет на производительность? Я использую это в ограниченных отчетах, так что в моем случае это не важно, но мне любопытно.
Адвилли
23
Почему это не ответ? Это именно то, что мне тоже нужно.
Арт Гайгель
7
@adjwilli Если столбец был частью индекса, вы пострадаете от снижения производительности запросов, зависящих от этого индекса. Чтобы поддерживать производительность, вам нужно изменить таблицу.
дшин
6
Что это будет делать для строк UTF-8, содержащих один и тот же символ с другим представлением, например, используя объединяющий символ для добавления умлаута? Эти строки UTF-8 можно рассматривать как равные: convert(char(0x65,0xcc,0x88) using utf8)(то есть eс ¨добавлением) и convert(char(0xc3,0xab) using utf8)(то есть ë), но добавление BINARYсделает их неравными.
mvds
3
В качестве примера производительности: мой запрос проходит от 3,5 мс (незначительный) до 1,570 мс (это примерно полторы секунды), запрашивая таблицу с 1,8 млн строк приблизительно.
Луис Суньол
64

Ответ, опубликованный Крейгом Уайтом, имеет большой штраф

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

потому что он не использует индексы. Таким образом, либо вам нужно изменить порядок сортировки таблиц, как указано здесь https://dev.mysql.com/doc/refman/5.7/en/case-sensitivity.html .

ИЛИ

Самое простое решение - использовать двоичный файл значения.

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

Например.

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 |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+

В.С.

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 ряд в наборе (0,00 сек)

Nitesh
источник
Это не чувствительно к регистру на 10.3.22-MariaDB (с использованием libmysql - 5.6.43)
user10398534
40

Вместо использования оператора = вы можете использовать LIKE или 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'

Это займет «а», а не «А» в своем состоянии

insoftservice
источник
Это не чувствительно к регистру на 10.3.22-MariaDB (с использованием libmysql - 5.6.43)
user10398534
17

Чтобы использовать индекс перед использованием BINARY, вы можете сделать что-то подобное, если у вас большие таблицы.

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

Подзапрос приведет к действительно небольшому нечувствительному к регистру подмножеству, из которого вы затем выбираете единственное чувствительное к регистру совпадение.

Эрик
источник
Стоит прокомментировать, что вышеизложенное поможет только в зависимости от ваших данных - ваш поиск без учета регистра потенциально может вернуть довольно большое подмножество данных.
BrynJ
15

Наиболее правильный способ сравнения строк с учетом регистра без изменения параметров сортировки запрашиваемого столбца - это явное указание набора символов и параметров сравнения для значения, с которым сравнивается столбец.

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

Почему бы не использовать binary?

Использование binaryоператора нецелесообразно, поскольку он сравнивает фактические байты кодированных строк. Если вы сравните фактические байты двух строк, закодированных с использованием разных наборов символов, две строки, которые следует считать одинаковыми, могут быть не равны. Например, если у вас есть столбец, который использует latin1набор символов, и ваш набор символов сервера / сеанса имеет значение utf8mb4, то при сравнении столбца со строкой, содержащей акцент, такой как 'café', он не будет совпадать со строками, содержащими эту же строку! Это потому, что в latin1é кодируется как байт, 0xE9но в utf8нем два байта:0xC3A9 .

Зачем использовать convertтак же, какcollate ?

Сопоставления должны соответствовать набору символов. Таким образом, если ваш сервер или сеанс настроен на использование latin1набора символов, который вы должны использовать, collate latin1_binно если ваш набор символов - utf8mb4вы должны использовать collate utf8mb4_bin. Поэтому наиболее надежное решение - всегда преобразовывать значение в наиболее гибкий набор символов и использовать двоичное сопоставление для этого набора символов.

Почему применяется значение convertи collateк значению, а не к столбцу?

Когда вы применяете любую функцию преобразования к столбцу перед выполнением сравнения, он не позволяет обработчику запросов использовать индекс, если он существует для столбца, что может значительно замедлить ваш запрос. Поэтому всегда лучше преобразовывать значение, где это возможно. Когда сравнение выполняется между двумя строковыми значениями, и одно из них имеет явно заданное сопоставление, механизм запросов будет использовать явное сопоставление независимо от того, к какому значению оно применяется.

Акцент Чувствительность

Важно отметить, что MySql не только нечувствителен к регистру столбцов, использующих параметры _ciсортировки (как правило, по умолчанию), но также нечувствителен к акценту . Это значит что 'é' = 'e'. Использование двоичного сопоставления (или binaryоператора) сделает сравнение строк чувствительным к акценту, а также к регистру.

Что такое utf8mb4?

Набор utf8символов в MySql - это псевдоним, для utf8mb3которого не рекомендуется в последних версиях, поскольку он не поддерживает 4-байтовые символы (что важно для кодирования строк, таких как 🐈). Если вы хотите использовать кодировку UTF8 с MySql, вам следует использовать utf8mb4кодировку.

Пол Уилер
источник
8

Следующее для версий MySQL, равных или выше, чем 5.5.

Добавить в /etc/mysql/my.cnf

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

Все другие сопоставления, которые я пробовал, казались нечувствительными к регистру, работал только "utf8_bin".

Не забудьте перезапустить mysql после этого:

   sudo service mysql restart

Согласно http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html есть также «latin1_bin».

"Utf8_general_cs" не был принят при запуске mysql. (Я читаю "_cs" как "чувствительный к регистру" - ???).

fritzthecat
источник
7

Вы можете использовать BINARY с учетом регистра, как это

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

к сожалению, этот sql не может использовать индекс, вы будете страдать от снижения производительности запросов, зависящих от этого индекса

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 |
+----+-------------+--------+------------+------+---------------+------+---------+------+---------+----------+-------------+

К счастью, у меня есть несколько хитростей, чтобы решить эту проблему

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 |
+----+-------------+--------+------------+------+---------------------------+---------------------------+---------+-------+------+----------+-----------------------+  
xiezefan
источник
Это не чувствительно к регистру на 10.3.22-MariaDB (с использованием libmysql - 5.6.43)
user10398534
2

Превосходно!

Я поделюсь с вами кодом из функции, которая сравнивает пароли:

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;
Виктор Энрике
источник
Нужно добавить declare pSuccess BINARY;в начале
Adinas
2

Не нужно ничего менять на уровне БД, просто нужно внести изменения в SQL Query, это будет работать.

Пример -

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

Двоичное ключевое слово сделает регистр чувствительным.

Папу Мехта
источник
1

По умолчанию mysql не чувствителен к регистру, попробуйте изменить языковую сортировку на latin1_general_cs

ohmusama
источник