如何在 MySQL 上进行 SQL 区分大小写的字符串比较?
Posted
技术标签:
【中文标题】如何在 MySQL 上进行 SQL 区分大小写的字符串比较?【英文标题】:How can I make SQL case sensitive string comparison on MySQL? 【发布时间】:2011-08-03 12:01:14 【问题描述】:我有一个函数可以返回五个混合大小写的字符。如果我对此字符串进行查询,无论大小写,它都会返回该值。
如何使 mysql 字符串查询区分大小写?
【问题讨论】:
dev.mysql.com/doc/refman/5.0/en/charset-binary-op.html 注意 BINARY 与区分大小写的比较不同: select 'à' like 'a' // 返回 true select 'à' like BINARY 'a' // 返回 false !!! select 'à' like 'a' COLLATE latin1_general_cs // 返回 true 所以使用 BINARY 进行区分大小写比较的建议是不正确的。 @cquezel:所以,你是说 [select 'à' like BINARY 'a'] 应该返回 true?无论如何,这与区分大小写的比较有什么关系? @FranciscoZarabozo 下面有人建议使用 BINARY 比较来进行区分大小写的比较。我只是指出,在其他语言中,这可能无法按预期工作,因为 BINARY 与区分大小写不同。 @cquezel 我认为“à”与“a”是不同的字母。所以无论如何,两者之间的比较确实应该是错误的。 【参考方案1】:好消息是,如果您需要进行区分大小写的查询,这很容易做到:
SELECT * FROM `table` WHERE BINARY `column` = 'value'
【讨论】:
这正是我想要的。如果可以的话,我会更高。但是有一个问题,这对性能有什么影响?我在有限的报告上使用它,所以对我来说并不重要,但我很好奇。 为什么这不是答案?这也正是我所需要的。 @adjwilli 如果该列是索引的一部分,则依赖于该索引的查询将受到性能影响。为了保持性能,您需要实际更改表。 这对于包含相同字符但具有不同表示的 UTF-8 字符串会做什么,例如使用组合字符添加变音符号?这些 UTF-8 字符串可以被视为相等:convert(char(0x65,0xcc,0x88) using utf8)
(即e
,添加了¨
)和convert(char(0xc3,0xab) using utf8)
(即ë
),但添加BINARY
会使它们不相等。
作为一个性能示例:我的查询从 3.5 毫秒(可以忽略不计)到 1.570 毫秒(大约一秒半),查询一个大约有 180 万行的表。【参考方案2】:
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
如果您希望始终以区分大小写的方式处理列,请使用区分大小写或二进制排序规则声明它。
【讨论】:
关于如何在 phpmyadmin 中执行此操作的任何提示? @StevenB:点击列的编辑按钮,然后设置排序规则 --> i.imgur.com/7SoEw.png @BT 要使 utf8 列区分大小写,您可以使用 bin 排序,例如:SELECT 'email' COLLATE utf8_bin = 'Email'
@drudge 您如何声明具有区分大小写排序规则的列?
@StephaneEybert 如果您正在寻找直接区分大小写的方法,我很幸运能在 ut8 表中的字段中使用 varbinary 而不是 varchar。 HTH【参考方案3】:
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) 我使用了 Craig White 的解决方案一年,但在几次页面加载投诉后,我进行了更深入的研究,做出了 Nitesh 推荐的更改,查询从 2.5 秒变为 0.15 秒。当 Binary 在 Where 之前时,它没有使用索引。将 Binary 移动到使用索引的位置之后。谢谢! 好主意Nitesh!这应该是票数最高的答案【参考方案4】:您可能想要使用 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)【参考方案5】:在不更改被查询列的排序规则的情况下执行区分大小写的字符串比较的最正确方法是显式指定要与列进行比较的值的字符集和排序规则。
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
排序规则(通常是默认值)的列不区分大小写,而且对 accent 不敏感。这意味着'é' = 'e'
。使用二进制排序规则(或 binary
运算符)将使字符串比较区分重音和区分大小写。
什么是utf8mb4
?
MySql 中的utf8
字符集是utf8mb3
的别名,它一直是deprecated in recent versions,因为它不支持4 字节字符(这对于编码像?这样的字符串很重要)。如果您希望在 MySql 中使用 UTF8 character encoding,那么您应该使用 utf8mb4
字符集。
【讨论】:
【参考方案6】:要在使用 BINARY 之前使用索引,如果你有大表,你可以这样做。
SELECT
*
FROM
(SELECT * FROM `table` WHERE `column` = 'value') as firstresult
WHERE
BINARY `column` = 'value'
子查询将产生一个非常小的不区分大小写的子集,然后您选择其中唯一区分大小写的匹配项。
【讨论】:
值得一提的是,上述内容仅对您的数据有所帮助 - 您的不区分大小写的搜索可能会返回相当大的数据子集。【参考方案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)【参考方案8】:以下适用于等于或高于 5.5 的 MySQL 版本。
添加到/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”。
mysql启动不接受“utf8_general_cs”。 (我将“_cs”读作“区分大小写” - ???)。
【讨论】:
【参考方案9】:无需更改数据库级别的任何内容,只需更改 SQL 查询即可。
例子-
"SELECT * FROM <TABLE> where userId = '" + iv_userId + "' AND password = BINARY '" + iv_password + "'";
二进制关键字会区分大小写。
【讨论】:
【参考方案10】:太棒了!
我与你分享一个比较密码的函数的代码:
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;
【参考方案11】:
mysql默认不区分大小写,尝试将语言排序改为latin1_general_cs
【讨论】:
【参考方案12】:对于那些希望使用 RLIKE
或 REGEXP
与正则表达式进行区分大小写比较的人,您可以改用 REGEXP_LIKE()
和匹配类型 c
,如下所示:
SELECT * FROM `table` WHERE REGEXP_LIKE(`column`, 'value', 'c');
【讨论】:
以上是关于如何在 MySQL 上进行 SQL 区分大小写的字符串比较?的主要内容,如果未能解决你的问题,请参考以下文章