Як я можу зробити порівняння рядків з урахуванням регістру SQL на MySQL?


285

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

Як я можу зробити рядки запитів MySQL у регістрі?



8
Зауважте, що BINARY - це не те саме порівняння з урахуванням регістру: виберіть 'à' як 'a' // повертає true true select 'à' як BINARY 'a' // повертає false! виберіть 'à' як 'a' COLLATE latin1_general_cs // повертає true, тому пропозиція використовувати BINARY для порівняння з урахуванням регістру невірна.
cquezel

3
@cquezel: Отже, ви говорите, що [select 'à' як BINARY 'a'] повинен повернути true ?? У будь-якому випадку, що це стосується порівнянь з урахуванням регістру?
Франциско Зарабобоцо

3
@FranciscoZarabozo деякі люди нижче запропонували використовувати BINARY порівняння, щоб зробити порівняльне врахування регістру. Я просто вказую, що в інших мовах це, ймовірно, не працюватиме так, як очікувалося, оскільки БІНАРІЯ - це не те саме, що залежить від регістру.
cquezel

3
@cquezel Я думаю, що "à" - це інша літера, ніж "a". Тож порівняння між цими двома справді має бути помилковим у будь-якому випадку.
Стефан

Відповіді:


159

http://dev.mysql.com/doc/refman/5.0/uk/case-sensibility.html

Набір символів та зіставлення за замовчуванням - latin1 та latin1_swedish_ci, тому порівняння рядків небінарних рядків за замовчуванням нечутливе до регістру. Це означає, що якщо здійснити пошук з іменем col_ 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?
СтівенБ

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
Андрій Т

724

Хороша новина полягає в тому, що якщо вам потрібно зробити запит з урахуванням регістру, це зробити дуже просто:

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

34
Це саме те, що я шукав. Я хотів би вище, якби міг. Питання, однак, який вплив це на продуктивність? Я використовую його на обмеженій звітності, тому в моєму випадку це не важливо, але мені цікаво.
adjwilli

23
Чому це не відповідь? Це саме те, що мені було потрібно.
Art Geigel

7
@adjwilli Якщо стовпець був частиною індексу, ви постраждаєте від ефективності на запити, що залежать від цього індексу. Для підтримки продуктивності потрібно фактично змінити таблицю.
dshin

6
Що це буде робити для рядків UTF-8, що містять один і той же символ з різним поданням, наприклад, використовуючи комбінуючий символ, щоб додати umlaut? Ці рядки 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М рядків.
Lluís Suñol

64

Відповідь, яку опублікував Крейг Уайт, має велику ефективність штрафу

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

тому що він не використовує індекси. Отже, або вам потрібно змінити зіставлення таблиці, як тут згадка https://dev.mysql.com/doc/refman/5.7/uk/case-sensibility.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 |
+----+-------------+--------+------+---------------+------+---------+------+--------+-------------+

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


Це , здається, не чутливі до регістру на 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'

У його стані знадобиться "а", а не "А"


Це , здається, не чутливі до регістру на 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-sensibility.html також існує "latin1_bin".

Запуск mysql не було прийнято "utf8_general_cs". (Я читаю "_cs" як "залежно від регістру" - ???).


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

Це , здається, не чутливі до регістру на 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.