Làm thế nào tôi có thể thực hiện so sánh chuỗi nhạy cảm trường hợp SQL trên MySQL?


285

Tôi có một hàm trả về năm ký tự với trường hợp hỗn hợp. Nếu tôi thực hiện một truy vấn trên chuỗi này, nó sẽ trả về giá trị bất kể trường hợp nào.

Làm thế nào tôi có thể làm cho trường hợp truy vấn chuỗi MySQL nhạy cảm?



8
Lưu ý rằng BINary không giống như so sánh phân biệt chữ hoa chữ thường: select 'à' like 'a' // trả về true select 'à' like BINary 'a' // trả về false !!! chọn 'à' like 'a' COLLATE latin1_general_cs // trả về true Vì vậy, đề xuất sử dụng BINary cho trường hợp so sánh phân biệt chữ hoa chữ thường là không chính xác.
cquezel

3
@cquezel: Vì vậy, bạn đang nói rằng [chọn 'à' như BINary 'a'] sẽ trả về đúng không ?? Trong mọi trường hợp, điều này có liên quan gì đến so sánh trường hợp nhạy cảm?
Francisco Zarabozo

3
@FranciscoZarabozo một số người dưới đây đã đề xuất sử dụng so sánh BINary để so sánh trường hợp nhạy cảm. Tôi chỉ chỉ ra rằng trong các ngôn ngữ khác, điều này có thể sẽ không hoạt động như mong đợi vì BINary không giống như trường hợp nhạy cảm.
cquezel

3
@cquezel Tôi sẽ nghĩ rằng 'à' là một chữ cái khác với 'a'. Vì vậy, so sánh giữa hai nên thực sự là sai bất kể trường hợp nào.
Stephane

Câu trả lời:


159

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

Bộ ký tự và đối chiếu mặc định là latin1 và latin1_swbur_ci, do đó, so sánh chuỗi không phân biệt là trường hợp không phân biệt theo mặc định. Điều này có nghĩa là nếu bạn tìm kiếm bằng col_name THÍCH 'a%', bạn sẽ nhận được tất cả các giá trị cột bắt đầu bằng A hoặc a. Để làm cho trường hợp tìm kiếm này nhạy cảm, hãy đảm bảo rằng một trong các toán hạng có đối chiếu trường hợp nhạy cảm hoặc nhị phân. Ví dụ: nếu bạn đang so sánh một cột và một chuỗi có cả bộ ký tự latin1, bạn có thể sử dụng toán tử COLLATE để làm cho toán hạng có đối chiếu latin1_general_cs hoặc 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

Nếu bạn muốn một cột luôn được xử lý theo kiểu phân biệt chữ hoa chữ thường, hãy khai báo nó với một đối chiếu nhị phân hoặc phân biệt chữ hoa chữ thường.


4
bất kỳ gợi ý về cách làm điều này trong phpmyadmin?
StevenB

4
@StevenB: Nhấp vào nút Chỉnh sửa của cột, sau đó đặt Collation -> i.imgur.com/7SoEw.png
drudge

32
@BT Để làm cho trường hợp cột utf8 nhạy cảm, bạn có thể sử dụng col colation như:SELECT 'email' COLLATE utf8_bin = 'Email'
piotrekkr

@drudge Làm thế nào bạn sẽ khai báo một cột với đối chiếu phân biệt chữ hoa chữ thường?
Stephane

1
@StephaneEybert nếu bạn đang tìm kiếm độ nhạy trường hợp thẳng, tôi đã gặp may mắn khi sử dụng varbinary thay vì varchar cho một trường trong bảng ut8. HTH
Andrew T

724

Tin tốt là nếu bạn cần thực hiện một truy vấn phân biệt chữ hoa chữ thường, thì rất dễ thực hiện:

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

34
Điều này thật đúng với gì mà tôi đã tìm kiếm. Tôi sẽ tăng nó lên nếu tôi có thể. Một câu hỏi mặc dù, điều này có ảnh hưởng gì đến hiệu suất? Tôi đang sử dụng nó trên một điều báo cáo hạn chế, vì vậy nó không quan trọng trong trường hợp của tôi, nhưng tôi tò mò.
adjwilli

23
Tại sao đây không phải là câu trả lời? Đây chính xác là những gì tôi cần quá.
Art Geigel

7
@adjwilli Nếu cột là một phần của chỉ mục, bạn sẽ phải chịu một cú đánh hiệu suất đối với các truy vấn phụ thuộc vào chỉ mục đó. Để duy trì hiệu suất, bạn cần thực sự thay đổi bảng.
dshin

6
Điều này sẽ làm gì đối với các chuỗi UTF-8 chứa cùng một ký tự có biểu diễn khác, ví dụ: sử dụng ký tự kết hợp để thêm âm sắc? Các chuỗi UTF-8 này có thể được coi là bằng nhau: convert(char(0x65,0xcc,0x88) using utf8)(nghĩa là e¨thêm) và convert(char(0xc3,0xab) using utf8)(nghĩa là ë), nhưng việc thêm BINARYsẽ làm cho chúng không bằng nhau.
mvds

3
Như một ví dụ về hiệu suất: truy vấn của tôi chuyển từ 3,5ms (không đáng kể) đến 1,570ms (đây là khoảng một giây rưỡi), truy vấn một bảng có 1,8 triệu hàng aprox.
Lluís Suñol

64

Trả lời được đăng bởi Craig White, có hiệu suất phạt lớn

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

bởi vì nó không sử dụng chỉ mục. Vì vậy, hoặc bạn cần thay đổi đối chiếu bảng như được đề cập ở đây https://dev.mysql.com/doc/refman/5.7/en/case-sens nhạy.html .

HOẶC LÀ

Sửa chữa dễ nhất, bạn nên sử dụng một giá trị BINary.

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

Ví dụ.

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 hàng trong bộ (0,00 giây)


Điều này dường như không phân biệt chữ hoa chữ thường trên 10.3.22-MariaDB (sử dụng libmysql - 5.6.43)
user10398534

40

Thay vì sử dụng toán tử =, bạn có thể muốn sử dụng THÍCH hoặc THÍCH 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'

Nó sẽ mất 'a' chứ không phải 'A' trong điều kiện của nó


Điều này dường như không phân biệt chữ hoa chữ thường trên 10.3.22-MariaDB (sử dụng libmysql - 5.6.43)
user10398534

17

Để sử dụng một chỉ mục trước khi sử dụng BINary, bạn có thể làm một cái gì đó như thế này nếu bạn có các bảng lớn.

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

Truy vấn con sẽ dẫn đến một tập hợp con không phân biệt chữ hoa chữ thường rất nhỏ mà sau đó bạn chọn đối sánh phân biệt chữ hoa chữ thường.


Thật đáng để bình luận khi nói rằng những điều trên sẽ chỉ giúp tùy thuộc vào dữ liệu của bạn - tìm kiếm không nhạy cảm trong trường hợp của bạn có thể có khả năng trả về một tập hợp dữ liệu khá lớn.
BrynJ

15

Cách chính xác nhất để thực hiện so sánh chuỗi phân biệt chữ hoa chữ thường mà không thay đổi đối chiếu của cột được truy vấn là chỉ định rõ ràng một bộ ký tự và đối chiếu cho giá trị mà cột đang được so sánh.

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

Tại sao không sử dụng binary?

Việc sử dụng binarytoán tử là không thể vì nó so sánh các byte thực tế của các chuỗi được mã hóa. Nếu bạn so sánh các byte thực tế của hai chuỗi được mã hóa bằng cách sử dụng các ký tự khác nhau thì hai chuỗi nên được coi là giống nhau, chúng có thể không bằng nhau. Ví dụ: nếu bạn có một cột sử dụng bộ latin1ký tự và bộ ký tự máy chủ / phiên của bạn là utf8mb4, thì khi bạn so sánh cột với một chuỗi có dấu như 'café', nó sẽ không khớp với các hàng có cùng chuỗi đó! Điều này là do trong latin1é được mã hóa dưới dạng byte 0xE9nhưng trong utf8đó là hai byte : 0xC3A9.

Tại sao sử dụng converttốt như collate?

Bộ sưu tập phải phù hợp với bộ ký tự. Vì vậy, nếu máy chủ hoặc phiên của bạn được đặt để sử dụng bộ latin1ký tự bạn phải sử dụng collate latin1_binnhưng nếu bộ ký tự của bạn là utf8mb4bạn phải sử dụng collate utf8mb4_bin. Do đó, giải pháp mạnh mẽ nhất là luôn chuyển đổi giá trị thành bộ ký tự linh hoạt nhất và sử dụng đối chiếu nhị phân cho bộ ký tự đó.

Tại sao áp dụng convertcollatecho giá trị mà không phải cột?

Khi bạn áp dụng bất kỳ chức năng chuyển đổi nào cho một cột trước khi thực hiện so sánh, nó sẽ ngăn công cụ truy vấn sử dụng một chỉ mục nếu có tồn tại cho cột, điều này có thể làm chậm đáng kể truy vấn của bạn. Do đó, luôn luôn tốt hơn để chuyển đổi giá trị thay vì có thể. Khi so sánh được thực hiện giữa hai giá trị chuỗi và một trong số chúng có đối chiếu được chỉ định rõ ràng, công cụ truy vấn sẽ sử dụng đối chiếu rõ ràng, bất kể giá trị nào được áp dụng.

Độ nhạy của giọng

Điều quan trọng cần lưu ý là MySql không chỉ không phân biệt chữ hoa chữ thường đối với các cột bằng cách sử dụng _ciđối chiếu (thường là mặc định), mà còn không có dấu không nhạy. Điều này có nghĩa là 'é' = 'e'. Sử dụng đối chiếu nhị phân (hoặc binarytoán tử) sẽ làm cho các phép so sánh chuỗi có độ nhạy cũng như phân biệt chữ hoa chữ thường.

utf8mb4

Bộ utf8ký tự trong MySql là một bí danh utf8mb3đã bị phản đối trong các phiên bản gần đây vì nó không hỗ trợ các ký tự 4 byte (rất quan trọng đối với các chuỗi mã hóa như). Nếu bạn muốn sử dụng mã hóa ký tự UTF8 với MySql thì bạn nên sử dụng bộ utf8mb4ký tự.


8

Sau đây là cho các phiên bản MySQL bằng hoặc cao hơn 5,5.

Thêm vào /etc/mysql/my.cnf

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

Tất cả các bộ sưu tập khác mà tôi đã thử dường như không phân biệt chữ hoa chữ thường, chỉ có "utf8_bin" hoạt động.

Đừng quên khởi động lại mysql sau này:

   sudo service mysql restart

Theo http://dev.mysql.com/doc/refman/5.0/en/case-sens nhạy.html cũng có một "latin1_bin".

"Utf8_general_cs" không được chấp nhận bởi khởi động mysql. (Tôi đọc "_cs" là "phân biệt chữ hoa chữ thường" - ???).


7

Bạn có thể sử dụng BINary cho trường hợp nhạy cảm như thế này

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

Thật không may, sql này không thể sử dụng chỉ mục, bạn sẽ bị ảnh hưởng bởi các truy vấn phụ thuộc vào chỉ mục đó

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

May mắn thay, tôi có một vài thủ thuật để giải quyết vấn đề này

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

Điều này dường như không phân biệt chữ hoa chữ thường trên 10.3.22-MariaDB (sử dụng libmysql - 5.6.43)
user10398534

2

Thông minh!

Tôi chia sẻ với bạn, mã từ một chức năng so sánh mật khẩu:

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;

Cần thêm declare pSuccess BINARY;vào lúc bắt đầu
adinas

2

Không cần thay đổi bất cứ điều gì ở cấp độ DB, chỉ cần bạn phải thay đổi trong Truy vấn SQL thì nó sẽ hoạt động.

Thí dụ -

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

Từ khóa nhị phân sẽ làm cho trường hợp nhạy cảm.


1

Theo mặc định, mysql không phân biệt chữ hoa chữ thường, hãy thử thay đổi đối chiếu ngôn ngữ thành latin1_general_cs

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.