Bagaimana saya bisa membuat perbandingan string case SQL sensitif pada MySQL?

285

Saya memiliki fungsi yang mengembalikan lima karakter dengan case campuran. Jika saya melakukan kueri pada string ini, ia akan mengembalikan nilai terlepas dari kasusnya.

Bagaimana cara membuat MySQL string kueri case sensitif?

StevenB
sumber
8
Perhatikan bahwa BINARY tidak sama dengan perbandingan case-sensitive: pilih 'à' like 'a' // mengembalikan true pilih 'à' seperti BINARY 'a' // mengembalikan false !!! pilih 'à' seperti 'a' COLLATE latin1_general_cs // mengembalikan true Jadi saran untuk menggunakan BINARY untuk case-sensitive bandingkan tidak benar.
cquezel
3
@cquezel: Jadi, Anda mengatakan bahwa [pilih 'à' seperti BINARY 'a'] harus mengembalikan true ?? Dalam kasus apa pun, apa hubungannya dengan perbandingan sensitif huruf besar-kecil?
Francisco Zarabozo
3
@FranciscoZarabozo, beberapa orang di bawah ini menyarankan untuk menggunakan perbandingan BINARY untuk melakukan perbandingan sensitif huruf. Saya hanya menunjukkan bahwa dalam bahasa lain, ini mungkin tidak akan berfungsi seperti yang diharapkan karena BINARY tidak sama dengan case sensitive.
cquezel
3
@queque Saya akan berpikir bahwa 'à' adalah huruf yang berbeda dari 'a'. Jadi perbandingan antara keduanya memang harus salah apa pun masalahnya.
Stephane

Jawaban:

159

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

Kumpulan karakter dan susunan default adalah latin1 dan latin1_swedish_ci, jadi perbandingan string non-biner tidak peka huruf besar-kecil. Ini berarti bahwa jika Anda mencari dengan col_name LIKE 'a%', Anda mendapatkan semua nilai kolom yang dimulai dengan A atau a. Untuk menjadikan case pencarian ini sensitif, pastikan bahwa salah satu operan memiliki collation case sensitive atau binary. Misalnya, jika Anda membandingkan kolom dan string yang keduanya memiliki set karakter latin1, Anda dapat menggunakan operator COLLATE untuk menyebabkan salah satu operan memiliki susunan latin1_general_cs atau 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

Jika Anda ingin kolom selalu diperlakukan dengan case-sensitive, mendeklarasikannya dengan case-sensitive atau binary collation.

membanting tulang
sumber
4
ada petunjuk tentang cara melakukan ini di phpmyadmin?
StevenB
4
@StevenB: Klik kolom tombol Edit, kemudian mengatur Fisik -> i.imgur.com/7SoEw.png
membanting tulang
32
@BT Untuk membuat sensitifitas huruf kolom utf8 Anda bisa menggunakan bin colation seperti:SELECT 'email' COLLATE utf8_bin = 'Email'
piotrekkr
@drudge Bagaimana Anda mendeklarasikan kolom dengan susunan case sensitif?
Stephane
1
@StephaneEybert jika Anda sedang mencari sensitivitas case langsung Saya beruntung menggunakan varbinary daripada varchar untuk bidang di tabel ut8. HTH
Andrew T
724

Berita baiknya adalah jika Anda perlu membuat kueri yang peka terhadap huruf besar-kecil, mudah dilakukan:

SELECT *  FROM `table` WHERE BINARY `column` = 'value'
Craig White
sumber
34
Ini persis apa yang saya cari. Saya akan naik lebih tinggi jika saya bisa. Namun pertanyaannya, apa dampaknya terhadap kinerja? Saya menggunakannya pada hal pelaporan terbatas, jadi tidak penting dalam kasus saya, tetapi saya ingin tahu.
adjwilli
23
Mengapa ini bukan jawabannya? Inilah yang saya butuhkan juga.
Seni Geigel
7
@adjwilli Jika kolom merupakan bagian dari indeks, Anda akan mengalami kinerja yang buruk pada kueri yang bergantung pada indeks itu. Untuk mempertahankan kinerja, Anda harus benar-benar mengubah tabel.
dshin
6
Apa yang akan dilakukan untuk string UTF-8 yang berisi karakter yang sama dengan representasi yang berbeda, misalnya menggunakan karakter kombinasi untuk menambahkan umlaut? String UTF-8 ini dapat diperlakukan sama: convert(char(0x65,0xcc,0x88) using utf8)(yaitu edengan ¨ditambahkan) dan convert(char(0xc3,0xab) using utf8)(yaitu ë), tetapi menambahkan BINARYakan membuat mereka tidak sama.
mvds
3
Sebagai contoh kinerja: kueri saya berpindah dari 3,5 ms (dapat diabaikan) ke 1,570 ms (ini sekitar satu setengah detik), meminta tabel dengan 1,8M baris aprox.
Lluís Suñol
64

Jawaban yang diposting oleh Craig White, memiliki penalti kinerja yang besar

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

karena tidak menggunakan indeks. Jadi, Anda harus mengubah susunan tabel seperti disebutkan di sini https://dev.mysql.com/doc/refman/5.7/id/case-sensitivity.html .

ATAU

Perbaikan termudah, Anda harus menggunakan BINARY dari nilai.

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

Misalnya.

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 baris dalam set (0,00 dtk)

Nitesh
sumber
Ini sepertinya tidak peka terhadap huruf 10.3.22-MariaDB (menggunakan libmysql - 5.6.43)
user10398534
40

Alih-alih menggunakan operator =, Anda mungkin ingin menggunakan LIKE atau 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'

Ini akan mengambil 'a' dan bukan 'A' dalam kondisinya

layanan dalam
sumber
Ini sepertinya tidak peka terhadap huruf 10.3.22-MariaDB (menggunakan libmysql - 5.6.43)
user10398534
17

Untuk menggunakan indeks sebelum menggunakan BINARY, Anda bisa melakukan sesuatu seperti ini jika Anda memiliki tabel besar.

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

Subquery akan menghasilkan subset case-insensitive yang sangat kecil yang kemudian Anda pilih satu-satunya yang cocok dengan case-sensitive.

Eric
sumber
Layak dikomentari untuk mengatakan bahwa di atas hanya akan membantu tergantung pada data Anda - pencarian case sensitif Anda berpotensi mengembalikan subkumpulan data yang agak besar.
BrynJ
15

Cara paling benar untuk melakukan perbandingan string peka huruf besar-kecil tanpa mengubah susunan kolom yang ditanyakan adalah dengan secara eksplisit menentukan rangkaian karakter dan susunan untuk nilai yang dibandingkan dengan kolom tersebut.

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

Kenapa tidak digunakan binary ?

Menggunakan binaryoperator tidak disarankan karena membandingkan byte aktual dari string yang disandikan. Jika Anda membandingkan byte sebenarnya dari dua string yang disandikan menggunakan karakter yang berbeda, set dua string yang harus dianggap sama mereka mungkin tidak sama. Sebagai contoh jika Anda memiliki kolom yang menggunakan latin1set karakter, dan set karakter server / sesi Anda utf8mb4, maka ketika Anda membandingkan kolom dengan string yang mengandung aksen seperti 'café' itu tidak akan cocok dengan baris yang berisi string yang sama! Hal ini karena di latin1é dikodekan sebagai byte 0xE9tetapi utf8itu adalah dua byte: 0xC3A9.

Mengapa menggunakan convertjuga collate?

Collations harus cocok dengan set karakter. Jadi jika server atau sesi Anda diatur untuk menggunakan latin1set karakter yang harus Anda gunakan collate latin1_bintetapi jika set karakter utf8mb4Anda harus Anda gunakan collate utf8mb4_bin. Oleh karena itu solusi yang paling kuat adalah dengan selalu mengubah nilai menjadi set karakter yang paling fleksibel, dan menggunakan susunan biner untuk set karakter itu.

Mengapa menerapkan convertdan collateke nilai dan bukan kolom?

Ketika Anda menerapkan fungsi transformasi apa pun ke kolom sebelum membuat perbandingan, itu mencegah mesin kueri dari menggunakan indeks jika ada untuk kolom, yang secara dramatis dapat memperlambat permintaan Anda. Oleh karena itu selalu lebih baik untuk mengubah nilai daripada jika memungkinkan. Ketika perbandingan dilakukan antara dua nilai string dan salah satunya memiliki susunan yang ditentukan secara eksplisit, mesin kueri akan menggunakan susunan eksplisit, terlepas dari nilai mana itu diterapkan.

Sensitivitas Aksen

Penting untuk dicatat bahwa MySql tidak hanya case-insensitive untuk kolom menggunakan _cicollation (yang biasanya merupakan default), tetapi juga accent insensitive. Ini artinya 'é' = 'e'. Menggunakan susunan biner (atau binaryoperator) akan membuat perbandingan string menjadi peka aksen dan juga peka huruf besar-kecil.

Apa utf8mb4?

Set utf8karakter di MySql adalah alias utf8mb3yang sudah tidak digunakan lagi dalam versi terbaru karena tidak mendukung karakter 4 byte (yang penting untuk pengkodean string seperti 🐈). Jika Anda ingin menggunakan pengkodean karakter UTF8 dengan MySql maka Anda harus menggunakan utf8mb4charset.

Paul Wheeler
sumber
8

Berikut ini untuk versi MySQL yang sama dengan atau lebih tinggi dari 5.5.

Tambahkan ke /etc/mysql/my.cnf

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

Semua pemeriksaan lain yang saya coba tampaknya tidak peka huruf besar-kecil, hanya "utf8_bin" yang berfungsi.

Jangan lupa untuk me-restart mysql setelah ini:

   sudo service mysql restart

Menurut http://dev.mysql.com/doc/refman/5.0/id/case-sensitivity.html ada juga "latin1_bin".

"Utf8_general_cs" tidak diterima oleh startup mysql. (Saya membaca "_cs" sebagai "case-sensitive" - ​​???).

fritzthecat
sumber
7

Anda dapat menggunakan BINARY untuk case sensitif seperti ini

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

sayangnya sql ini tidak dapat menggunakan indeks, Anda akan mengalami masalah kinerja pada kueri yang bergantung pada indeks itu

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

Untungnya, saya punya beberapa trik untuk menyelesaikan masalah ini

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
sumber
Ini tampaknya tidak peka terhadap huruf 10.3.22-MariaDB (menggunakan libmysql - 5.6.43)
user10398534
2

Luar biasa!

Saya berbagi dengan Anda, kode dari fungsi yang membandingkan kata sandi:

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
sumber
Perlu menambahkan declare pSuccess BINARY;di awal
adinas
2

Tidak perlu mengubah apa pun pada tingkat DB, cukup Anda harus mengubah SQL Query itu akan berhasil.

Contoh -

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

Kata kunci biner akan membuat case sensitif.

Pappu Mehta
sumber
1

mysql tidak peka huruf besar-kecil, coba ubah susunan bahasa menjadi latin1_general_cs

ohmusama
sumber