一条诡异的insert语句
问题背景
有同事反馈在mysql上面执行一条普通的insert语句,结果报错,
execute failed due to >>> Incorrect string value: '\xA1;offl...' for
column 'biz_info' at row 1
经过半天的折腾,终于搞清楚了来龙去脉,这里简单给大家分享下。为了方便说明,我将测试例子中的表和语句简化,但不影响问题重现。
问题复现
连接字符集:UTF8
表结构:
CREATE TABLE `ggg` (
`id` int(11) DEFAULT NULL,
`c` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
root@test 06:13:48>insert into ggg
values(1,concat('cardName:校园网',char(59),'offlineCardType:campus'));
Query OK, 1 row affected, 1 warning (2.51
sec)
root@test 06:14:36>show warnings\G
*************************** 1. row
***************************
Level: Warning
Code: 1366
Message: Incorrect string value:
'\x91;offl...' for column 'c' at row 1
查看结果
root@test 06:16:06>select * from ggg
where id=1;
*************************** 1. row
***************************
id: 1
c: cardName:鏍″洯缃
问题分析
从报错的结果来看,感觉是字符集转换引起的问题,而且由于连接串的字符集是UTF8,表的字符集是GBK,更容易引起怀疑。但是,即使是字符集转换,也不应该导致插入报错,因为语句中的中文字符“校园网"都是普通汉字,UTF8->GBK不应该存在问题。那我们在回过头来看看insert语句,唯一特殊的是使用了concat和char两个函数。会不会跟这两个函数有关系?char(59)实际是字符“;”,为了验证想法,做了两个实验:
- 将char(59)替换成';'
insert into ggg values(1,concat('cardName:校园网',';','offlineCardType:campus'));
2.设置连接串字符集为GBK
insert into ggg
values(1,concat('cardName:校园网',char(59),'offlineCardType:campus'));
果然,两种情况执行结果都是OK的,查询结果如下:
root@test 09:22:32>select * from
ggg\G
*************************** 1. row
***************************
id: 1
c: cardName:鏍″洯缃
*************************** 2. row
***************************
id: 1
c: cardName:校园网;offlineCardType:campus
*************************** 3. row
***************************
id: 1
c: cardName:校园网;offlineCardType:campus
跟踪了下源代码,找到了原因。char()函数返回的是一个binary类型字符串,在进行concat时,会导致'cardName:校园网'字符串到binary的转换。转换前,mysql将字符串‘cardName:校园网’看作是9个英文字符和3个汉字字符;转换后,mysql将其看作是18个字节的二进制串,其中,UTF8字符集的三个汉字“校园网”占了9个字节。由于目标表字符集是GBK,因此在入库时,还会发生一次binary到GBK的转码,“校园网”的二级制编码是E6A0A1 E59BAD E58DA1,在转码过程中,由于GBK字符集只包含一个字节(编码值<128)和二个字节的字符(汉字和特殊字符),“校园网”的二进制串会按照两个字节拆分E6A0 A1E5 9BAD E58D A1,前面四个变为“鏍″洯缃”,解析到A1时,由于A1既不是单字节字符,又不能与后面的字节组成一个合法的GBK字符,导致转换出错。
现在就很好解释为啥改变语句后,两种情况都OK了。第一种情况,将char(59)直接替换成‘;’,由于不涉及UF8到binary的转换,只有utf8到gbk转码的过程,这个转换是OK的,不会出现乱码;第二种情况,将连接串的字符集设置为GBK,那么会涉及GBK到binary的转换,然后再从binary转换到GBK,由于整个转换过程并没有二进制数据丢失,所以也是OK的。
问题产生的两个关键点
- 连接字符集与表字符集不匹配
- 使用了char函数
解决办法
1.char函数提供了using语法来实现返回特定字符集的字符串,比如:char(59 using utf8)
2.保证连接字符集与表字符集一致。
一条诡异的insert语句的更多相关文章
- 24_MySQL插入insert语句
本节涉及SQL语句: -- MYSQL 基础操作 1.插入insert语句 INSERT INTO t_dept(deptno,dname,loc) VALUES(70,"后勤部" ...
- 一条insert语句批量插入多条记录
一条insert语句批量插入多条记录 常见的insert语句,向数据库中,一条语句只能插入一条数据: insert into persons (id_p, lastname , firstName, ...
- laravel 获取上一条insert语句产生的id
<?php //頭部引入DB類 use Illuminate\Support\Facades\DB; //在方法中獲取获取上一条insert语句产生的id $id = DB::getPdo()- ...
- 跟随一条insert语句, 进入TiDB的源码世界(上)
TiDB是Google F1的开源实现: TiDB实现了基于mvcc的乐观锁,在线表结构变更,基于时间戳的数据线性一致性,等等: 为了可靠性,TiDB和Oracle一样,维护了百万级别的自动化测试用例 ...
- Sql Server系列:Insert语句
1 INSERT语法 [ WITH <common_table_expression> [ ,...n ] ] INSERT { [ TOP ( expression ) [ PERCEN ...
- 如何判断一条sql(update,delete)语句是否执行成功
如何判断一条sql(update,delete)语句是否执行成功 catch (SQLException e) { } catch不到错误应该就成功了. ============== ...
- sql插入多条数据的sql语句
sql插入多条数据的sql语句 有三种方法:1.InSert Into <表名>(列名)Select <列名>From <源表名>如:INSERT INTO Ton ...
- SQL Insert语句数据以以unicode码存储 解决存储数据出现乱码的问题
写了个读取原始的文本数据导入数据库的工具 ,最后发现空中有几个值是乱码 例如 原始数据是 :Bjørn 存到数据库中是 Bj?rn 研究半天发现是一直以来忽略了一个标记‘N’ 2条 Insert 语句 ...
- 老李分享:MySql的insert语句的性能优化方案
老李分享:MySql的insert语句的性能优化方案 性能优化一直是测试人员比较感兴趣的内容,poptest在培训学员的时候也加大了性能测试调优的方面的内容,而性能优化需要经验的积累,经验的积累依 ...
随机推荐
- java中获取文件或文件夹的路径方法
获取当前类的所在工程路径; 如果不加"/" File f = new File(this.getClass().getResource("").getPath( ...
- mysql存储过程中的异常处理
http://www.cnblogs.com/cookiehu/p/4994278.html 定义异常捕获类型及处理方法: DECLARE handler_action HANDLER FOR con ...
- jdk1.6与Myeclipse的冲突造成的
出现这样的错误时:ERROR:JDWP Unable to get JNI 1.2 environment ,jvm-> GetEvn() return =- ...
- php中读写excel表格文件示例。
测试环境:php5.6.24.这块没啥兼容问题. 需要更多栗子,请看PHPExcel的examples.还是蛮强大的. 读取excel文件. 第一步.下载开源的PHPExcel的类库文件,官方网站是h ...
- loadrunner录制回放常见问题及解决办法
1.录制错误 1)录制本机 WebTours 或录制本地网站无法打开 当 使 用 Vista 以 后 的 操 作 系 统 时 , 会 出 现 该 问 题 . 这 是 由 于 在 本 地Wi ...
- spring常用注解使用解析
spring没有采用约定优于配置的策略,spring要求显示指定搜索哪些路径下的Java文件.spring将会把合适的java类全部注册成spring Bean. 问题:spring怎么知道把哪些 ...
- angular学习的一些小笔记(中)之directive
directive里面的几个配置,上代码就清晰了 <!DOCTYPE html> <html ng-app='app'> <head> <meta chars ...
- JS的window.location应用实例
window.location 对象用于获得当前页面的地址 (URL),并把浏览器重定向到新的页面. Window Locationwindow.location 对象在编写时可不使用 window ...
- 高性能javascript学习笔记系列(4) -算法和流程控制
参考高性能javascript for in 循环 使用它可以遍历对象的属性名,但是每次的操作都会搜索实例或者原型的属性 导致使用for in 进行遍历会产生更多的开销 书中提到不要使用for in ...
- Web数据持久化存储IndexedDB(不常用)
IndexedDB是在浏览器中保存结构化数据的一种数据库,为了替换WebSQL(标准已废弃,但被广泛支持)而出现.IndexedDB使用NoSQL的形式来操作数据库,保存和读取是JavaScript对 ...