ฉันจะทำการเปรียบเทียบสตริงตัวพิมพ์เล็ก - ใหญ่ของ SQL ใน MySQL ได้อย่างไร


285

ฉันมีฟังก์ชั่นที่คืนค่าตัวอักษรห้าตัวพร้อมตัวพิมพ์เล็ก ถ้าฉันทำแบบสอบถามในสายนี้มันจะคืนค่าโดยไม่คำนึงถึงกรณี

ฉันจะทำให้สตริงเคียวรี MySQL เป็นตัวพิมพ์เล็กหรือใหญ่ได้อย่างไร?



8
โปรดสังเกตว่า BINARY ไม่เหมือนกับการเปรียบเทียบขนาดตัวพิมพ์เล็กและใหญ่: เลือก 'à' เช่น 'a' // คืนค่าจริงเลือก 'à' เช่น BINARY 'a' // คืนค่าเท็จ !!! เลือก 'à' เช่น 'a' COLLATE latin1_general_cs // คืนค่าจริงดังนั้นคำแนะนำการใช้ BINARY สำหรับการเปรียบเทียบแบบตัวพิมพ์เล็กและใหญ่ไม่ถูกต้อง
cquezel

3
@cquezel: ดังนั้นคุณกำลังบอกว่า [เลือก 'à' เช่น BINARY 'a'] ควรกลับมาจริงหรือเปล่า ?? ไม่ว่าในกรณีใดสิ่งนี้เกี่ยวข้องกับการเปรียบเทียบขนาดตัวพิมพ์เล็กหรือใหญ่
Francisco Zarabozo

3
@FranciscoZarabozo บางคนด้านล่างแนะนำให้ใช้การเปรียบเทียบแบบไบนารีเพื่อทำการเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ ฉันแค่ชี้ให้เห็นว่าในภาษาอื่นสิ่งนี้อาจไม่ทำงานอย่างที่คาดไว้เนื่องจาก BINARY ไม่เหมือนกับตัวพิมพ์เล็กและตัวพิมพ์ใหญ่
cquezel

3
@cquezel ฉันคิดว่า 'à' เป็นตัวอักษรที่แตกต่างจาก 'a' ดังนั้นการเปรียบเทียบระหว่างทั้งสองควรเป็นเท็จไม่ว่ากรณีใด
Stephane

คำตอบ:


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
กรำ

32
@BT เพื่อให้คอลัมน์ตัวพิมพ์เล็กและใหญ่เหมาะสมคุณสามารถใช้ bin colation เช่น:SELECT 'email' COLLATE utf8_bin = 'Email'
piotrekkr

@drudge คุณจะประกาศคอลัมน์ด้วยการเปรียบเทียบขนาดตัวพิมพ์เล็กและใหญ่ได้อย่างไร
Stephane

1
@StephaneEybert หากคุณกำลังมองหากรณีเล็ก ๆ น้อย ๆ ฉันมีโชคในการใช้ varbinary แทน varchar สำหรับเขตข้อมูลในตาราง ut8 HTH
Andrew T

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)(ie ë) แต่การเพิ่มBINARYจะทำให้พวกเขาไม่เท่ากัน
mvds

3
เป็นตัวอย่างประสิทธิภาพ: แบบสอบถามของฉันส่งผ่านจาก 3,5ms (เล็กน้อย) ถึง 1.570ms (นี่คือประมาณหนึ่งและครึ่ง), สอบถามตารางด้วย 1.8M แถว aprox
LluísSuñol

64

คำตอบที่โพสต์โดย Craig White มีโทษประสิทธิภาพที่ยอดเยี่ยม

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

เพราะมันไม่ได้ใช้ดัชนี ดังนั้นทั้งคุณจำเป็นต้องเปลี่ยนการเปรียบเทียบตารางเช่นพูดถึงที่นี่https://dev.mysql.com/doc/refman/5.7/en/case-sensitivity.html

หรือ

การแก้ไขที่ง่ายที่สุดคุณควรใช้ค่า BINARY

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'

มันจะใช้ 'a' และไม่ใช่ '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แต่มันเป็นไบต์ที่สอง:utf80xC3A9

ทำไมต้องใช้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" ในฐานะ "ตัวพิมพ์เล็ก - ใหญ่" - ???)


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

ไม่จำเป็นต้องเปลี่ยนแปลงอะไรในระดับ DB เพียงคุณต้องเปลี่ยนใน SQL Query มันจะทำงาน

ตัวอย่าง -

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

คำหลักไบนารีจะทำให้ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่


1

mysql ไม่คำนึงถึงขนาดตัวพิมพ์โดยค่าเริ่มต้นลองเปลี่ยนการจัดเรียงภาษาเป็น latin1_general_cs

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.