Jak mogę porównać ciąg znaków SQL z rozróżnianiem wielkości liter w MySQL?

285

Mam funkcję, która zwraca pięć znaków wielkimi literami. Jeśli wykonam zapytanie na tym ciągu, zwróci ono wartość bez względu na wielkość liter.

Jak ustawić rozróżnianie wielkości liter w zapytaniach MySQL?

StevenB
źródło
8
Zauważ, że BINARY nie jest tym samym co porównanie z rozróżnianiem wielkości liter: wybierz „à” jak „a” // zwraca true wybierz „à” jak BINARY ”a„ // zwraca false !!! wybierz „à” lubię „a” ZBIERZ latin1_general_cs // zwraca true Tak więc propozycja użycia BINARY dla porównania z rozróżnianiem wielkości liter jest nieprawidłowa.
cquezel,
3
@cquezel: Więc mówisz, że [wybierz 'à' jak BINARY 'a'] powinno zwrócić true? W każdym razie, co to ma wspólnego z porównaniami uwzględniającymi wielkość liter?
Francisco Zarabozo,
3
@FranciscoZarabozo niektóre osoby poniżej sugerują użycie porównania BINARNEGO w celu porównania wielkości liter. Po prostu wskazuję, że w innych językach prawdopodobnie nie będzie to działało zgodnie z oczekiwaniami, ponieważ BINARY nie jest tym samym co rozróżniana wielkość liter.
cquezel
3
@cquezel Myślę, że „à” to inna litera niż „a”. Tak więc porównanie między nimi powinno być fałszywe, bez względu na przypadek.
Stephane

Odpowiedzi:

159

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

Domyślnym zestawem znaków i zestawieniem są latin1 i latin1_swedish_ci, więc porównania niebinarnych ciągów znaków domyślnie nie uwzględniają wielkości liter. Oznacza to, że jeśli wyszukujesz z col_name LIKE 'a%', otrzymujesz wszystkie wartości kolumn zaczynające się od A lub a. Aby rozróżnić wielkość liter w wyszukiwaniu, upewnij się, że jeden z operandów ma rozróżnianie wielkości liter lub sortowanie binarne. Na przykład, jeśli porównujesz kolumnę i ciąg znaków, które oba mają zestaw znaków latin1, możesz użyć operatora COLLATE, aby spowodować, że albo operand będzie miał zestawienie latin1_general_cs lub 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

Jeśli chcesz, aby kolumna była zawsze traktowana z uwzględnieniem wielkości liter, zadeklaruj ją za pomocą sortowania z rozróżnianiem wielkości liter lub binarnym.

popychadło
źródło
4
jakaś wskazówka jak to zrobić w phpmyadmin?
StevenB
4
@StevenB: Kliknij przycisk Edytuj w kolumnie, a następnie ustawić Sortowanie -> i.imgur.com/7SoEw.png
drudge
32
@BT Aby rozróżnić wielkość liter w kolumnie utf8, można użyć sortowania bin, takiego jak:SELECT 'email' COLLATE utf8_bin = 'Email'
piotrekkr
@drudge Jak zadeklarować kolumnę z sortowaniem z uwzględnieniem wielkości liter?
Stephane
1
@StephaneEybert, jeśli szukasz prostego rozróżniania wielkości liter, miałem szczęście w używaniu varbinary zamiast varchar dla pola w tabeli ut8. HTH
Andrew T
724

Dobrą wiadomością jest to, że jeśli chcesz wykonać rozróżnianie wielkości liter, bardzo łatwo jest zrobić:

SELECT *  FROM `table` WHERE BINARY `column` = 'value'
Craig White
źródło
34
Właśnie tego szukałem. Zrobiłbym to wyżej, gdybym mógł. Pytanie jednak, jaki to ma wpływ na wydajność? Używam go do ograniczonego raportowania, więc w moim przypadku nie jest to ważne, ale jestem ciekawy.
adjwilli
23
Dlaczego to nie jest odpowiedź? Tego właśnie potrzebowałem.
Art Geigel
7
@adjwilli Jeśli kolumna była częścią indeksu, wystąpi spadek wydajności zapytań zależnych od tego indeksu. Aby utrzymać wydajność, musisz zmienić tabelę.
dshin,
6
Co to zrobi dla ciągów UTF-8 zawierających ten sam znak z inną reprezentacją, np. Używając znaku łączącego, aby dodać umlaut? Te łańcuchy UTF-8 można traktować jako równe: convert(char(0x65,0xcc,0x88) using utf8)(tj. eZ ¨dodanym) i convert(char(0xc3,0xab) using utf8)(tj. ë), Ale dodanie BINARYspowoduje, że będą nierówne.
mvds
3
Jako przykład wydajności: moje zapytanie przechodzi z 3,5 ms (nieistotne) do 1,570 ms (jest to około półtorej sekundy), odpytując tabelę o około 1,8 mln wierszy.
Lluís Suñol
64

Odpowiedź wysłana przez Craiga White'a ma wysoką karę wydajności

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

ponieważ nie używa indeksów. Więc albo musisz zmienić układ tabeli, jak wspomniano tutaj https://dev.mysql.com/doc/refman/5.7/en/case-sensitivity.html .

LUB

Najłatwiejsza poprawka, powinieneś użyć BINARNEJ wartości.

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

Na przykład.

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 rząd w zestawie (0,00 s)

Nitesh
źródło
Wydaje się, że nie rozróżnia wielkości liter w 10.3.22-MariaDB (przy użyciu libmysql - 5.6.43)
10398534
40

Zamiast operatora = możesz użyć LIKE lub 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'

W swoim stanie zajmie „a”, a nie „A”

nieoficjalna obsługa
źródło
Wydaje się, że nie rozróżnia wielkości liter w 10.3.22-MariaDB (przy użyciu libmysql - 5.6.43)
10398534
17

Aby skorzystać z indeksu przed użyciem BINARY, możesz zrobić coś takiego, jeśli masz duże tabele.

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

Podkwerenda spowodowałoby naprawdę mały podzbiór bez rozróżniania wielkości liter, którego następnie wybrałeś jedyne dopasowanie z rozróżnianiem wielkości liter.

Eric
źródło
Warto skomentować, że powyższe informacje pomogą tylko w zależności od danych - wyszukiwanie bez rozróżniania wielkości liter może potencjalnie zwrócić dość duży podzbiór danych.
BrynJ,
15

Najodpowiedniejszym sposobem przeprowadzenia porównania łańcucha z rozróżnianiem wielkości liter bez zmiany sortowania kolumny, której dotyczy zapytanie, jest jawne określenie zestawu znaków i zestawienia dla wartości, z którą porównywana jest kolumna.

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

Dlaczego nie użyć binary?

Korzystanie z binaryoperatora jest niewskazane, ponieważ porównuje rzeczywiste bajty zakodowanych ciągów. Jeśli porównasz rzeczywiste bajty dwóch ciągów kodowanych przy użyciu różnych zestawów znaków, dwa ciągi, które należy uznać za takie same, mogą nie być równe. Na przykład, jeśli masz kolumnę korzystającą z latin1zestawu znaków, a zestaw znaków serwer / sesja to utf8mb4, to kiedy porównasz kolumnę z ciągiem zawierającym akcent, taki jak „kawiarnia”, nie będzie pasować do wierszy zawierających ten sam ciąg! To dlatego, że w latin1E jest kodowane jako bajt 0xE9, ale w utf8to dwa bajty: 0xC3A9.

Dlaczego warto korzystać convertrównie dobrze collate?

Sortowanie musi pasować do zestawu znaków. Więc jeśli twój serwer lub sesja jest ustawiona na używanie latin1zestawu znaków, musisz użyć, collate latin1_binale jeśli twój zestaw znaków jest taki utf8mb4, musisz użyć collate utf8mb4_bin. Dlatego najbardziej niezawodnym rozwiązaniem jest zawsze przekształcenie wartości w najbardziej elastyczny zestaw znaków i użycie binarnego zestawienia dla tego zestawu znaków.

Po co stosować wartość converti collatedo wartości, a nie do kolumny?

Zastosowanie dowolnej funkcji transformacji do kolumny przed porównaniem uniemożliwia silnikowi zapytań użycie indeksu, jeśli taki istnieje dla kolumny, co może znacznie spowolnić zapytanie. Dlatego zawsze lepiej jest, jeśli to możliwe, przekształcić wartość. Po przeprowadzeniu porównania dwóch wartości ciągów, a jedna z nich ma jawnie określone sortowanie, aparat zapytań użyje jawnego sortowania, niezależnie od tego, do której wartości zostanie zastosowane.

Czułość akcentu

Należy zauważyć, że MySql nie tylko nie rozróżnia wielkości liter w kolumnach korzystających z _cisortowania (co zwykle jest ustawieniem domyślnym), ale także nie uwzględnia rozróżniania akcentów . To znaczy że 'é' = 'e'. Użycie binarnego sortowania (lub binaryoperatora) sprawi, że porównania ciągów będą rozróżniać zarówno akcent, jak i wielkość liter.

Co to jest utf8mb4?

Zestaw utf8znaków w MySql to alias, utf8mb3który został przestarzały w najnowszych wersjach, ponieważ nie obsługuje 4 bajtów znaków (co jest ważne przy kodowaniu ciągów takich jak 🐈). Jeśli chcesz używać kodowania znaków UTF8 z MySql, powinieneś używać utf8mb4zestawu znaków.

Paul Wheeler
źródło
8

Poniżej podano wersje MySQL równe lub wyższe niż 5,5.

Dodaj do /etc/mysql/my.cnf

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

Wszystkie inne zestawienia, które próbowałem, wydawały się nie uwzględniać wielkości liter, działało tylko „utf8_bin”.

Nie zapomnij ponownie uruchomić mysql po tym:

   sudo service mysql restart

Według http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html istnieje również „latin1_bin”.

„Utf8_general_cs” nie został zaakceptowany podczas uruchamiania mysql. (Czytam „_cs” jako „rozróżnia małe i wielkie litery” - ???).

fritzthecat
źródło
7

Możesz użyć BINARY do rozróżniania wielkości liter w ten sposób

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

niestety ten sql nie może używać indeksu, będziesz odczuwał spadek wydajności zapytań zależnych od tego indeksu

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

Na szczęście mam kilka sztuczek, aby rozwiązać ten problem

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
źródło
To nie wydaje się być rozróżniana na 10.3.22-MariaDB (używając libmysql - 5.6.43)
user10398534
2

Doskonały!

Udostępniam Ci kod z funkcji porównującej hasła:

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;
Victor Enrique
źródło
Trzeba dodać declare pSuccess BINARY;na początku
adinas
2

Nie trzeba nic zmieniać na poziomie DB, wystarczy zmienić zapytanie SQL, to zadziała.

Przykład -

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

Binarne słowo kluczowe rozróżnia małe i wielkie litery.

Pappu Mehta
źródło
1

Domyślnie mysql nie rozróżnia wielkości liter, spróbuj zmienić sortowanie językowe na latin1_general_cs

ohmusama
źródło