在某次数据处理过程中,需要将所有的英文括号"()"替换成中文括号"()",处理的过程中发现在进行 or 条件判断时,条件的顺序不同会导致查询的结果不一致,但是同样的sql在 mysql 5.7 中是可以正常查询出结果的,于是就做了一下实验。
1、创建数据表
CREATE TABLE `mytable` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'PK,自增,企业ID',
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '企业名称',
PRIMARY KEY (`id`),
KEY `ux_name_isdelete` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC
2、插入实验数据
INSERT INTO `mytable` (`id`,`name` )VALUES (61405,'test1英文括号d()ceshi');
INSERT INTO `mytable` (`id`,`name`) VALUES (12706,'蓝莓d()英文括号');
3、前置条件
在进行查询前,我们先进行以下几点确认:
- 数据库的编码为 utf8
- 客户端连接的编码为 utf8mb4
4、字符和字节
简单来说字符就是不管中英文你数这个字符串有多少就是多少个字符。而字节就不一样了,不同的编码会影响它的长度。在utf8编码中,中文是占3个字节,其他数字、英文、符号占一个字节;而utf8mb4编码中,中文占3个字节,emoji符号、复杂的文字、繁体字占4个字节。
select char_length('test1英文括号d()ceshi🍄') as 字符,length('test1英文括号d()ceshi🍄') as 字节;
结果:
上述字符串中包含6个中文、11个英文+数字、1个emoji,所以在计算字节时,结果为:6*3 + 11 + 4 = 33。
5、实验
1)单纯的括号查询
-- 前英文后中文
select '('='(';
结果:
select '()'='()'
结果:
select '()'='()'
结果:
2)英文汉字混合查询
select 'test1英文括号d()ceshi' = 'test1英文括号d()ceshi'
3)做where条件查询
- 条件 a :
SELECT * FROM mytable WHERE name= 'test1英文括号d()ceshi';
结果:
- 条件 b:
SELECT * FROM mytable WHERE name= 'test1英文括号d()ceshi';
结果:
进行字符替换后,我们将中文替换为英文(即上述条件 a)放在 or 条件之前:
SELECT name
,replace( replace('test1英文括号d()ceshi', "(", "("), ")", ")") a
,replace(replace('test1英文括号d()ceshi', "(", "("), ")", ")") b
FROM mytable
WHERE
( name= replace( replace('test1英文括号d()ceshi', "(", "("), ")", ")")) -- a
or (name= replace( replace('test1英文括号d()ceshi', "(", "("), ")", ")")) -- b
;
结果:
进行字符替换后,我们将英文替换为中文(即上述条件 b)放在 or 条件之前:
SELECT name
,replace( replace('test1英文括号d()ceshi', "(", "("), ")", ")") a
,replace( replace('test1英文括号d()ceshi', "(", "("), ")", ")") b
FROM mytable
WHERE
(name= replace( replace('test1英文括号d()ceshi', "(", "("), ")", ")")) -- b
or
( name= replace(replace('test1英文括号d()ceshi', "(", "("), ")", ")")) -- a
;
结果:
从上述的结果可以看出,当服务器编码为 utf8 客户端编码为 utf8mb4 时,中文和英文的字符串被认为是相同的,但是当把这个字符串去和 uf8 编码的表中数据进行比较时是被判定为不同的。
要解决这种问题需要统一客户端与服务端的编码:
- 将客户端的链接设置为 utf8
set names utf8;
- 将数据表设置为utf8mb4
ALTER TABLE `mytable`
DEFAULT CHARACTER SET=utf8mb4,
COLLATE=utf8mb4_unicode_ci,
MODIFY COLUMN `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '企业名称' AFTER `id`
;
关于更多编码的内容可以查看 Mysql 8.0 的官方说明文档:
https://dev.mysql.com/doc/refman/8.0/en/charset-mysql.html
https://dev.mysql.com/doc/refman/8.0/en/charset-unicode-utf8mb4.html