MySQL字符集转换引发插入乱码问题
根据http://www.cnblogs.com/cchust/p/4601536.html进行验证测试
问题背景
在mysql上面执行一条普通的insert语句,结果报错:
- Incorrect string value: '\x91;offl...' for column 'c' at row 1
重现:
1)连接MySQL字符集是UTF8
- mysql --default-character-set=utf8 test
2)表结构
- CREATE TABLE `abc` (
- `id` int(11) DEFAULT NULL,
- `c` varchar(100) DEFAULT NULL
- ) ENGINE=InnoDB DEFAULT CHARSET=gbk
3)SQL
- jinyizhou@localhost : test 05:58:25>insert into abc values(1,'我们');
- Query OK, 1 row affected (0.01 sec)
- jinyizhou@localhost : test 05:58:32>insert into abc values(2,concat('cardName:校园网',char(59),'offlineCardType:campus'));
- Query OK, 1 row affected, 1 warning (0.00 sec) #报错
- jinyizhou@localhost : test 05:58:51>show warnings;
- +---------+------+----------------------------------------------------------------+
- | Level | Code | Message |
- +---------+------+----------------------------------------------------------------+
- | Warning | 1366 | Incorrect string value: '\x91;offl...' for column 'c' at row 1 |
- +---------+------+----------------------------------------------------------------+
- 1 row in set (0.00 sec)
- jinyizhou@localhost : test 05:58:55>select * from abc;
- +------+-----------------------+
- | id | c |
- +------+-----------------------+
- | 1 | 我们 |
- | 2 | cardName:鏍″洯缃 |
- +------+-----------------------+
- 2 rows in set (0.01 sec)
分析:
上面看到第2个插入乱码了,八九不离十是字符集的问题了,看warnings就知道。这里有疑问,为什么第一条插入正常,第二条就报错?2个插入的字符集都是一样的(客户端字符集),要是字符集出错,第一个也会报错。即使表的字符集是gbk,连接字符集是utf8,大家都知道utf8字符集范围要大于gbk,难道是”校内网“这3个中文超出了gbk范围?测试下:
- jinyizhou@localhost : test 05:59:02>insert into abc values(1,'校园网');
- Query OK, 1 row affected (0.00 sec)
- jinyizhou@localhost : test 06:04:12>select * from abc;
- +------+-----------------------+
- | id | c |
- +------+-----------------------+
- | 1 | 我们 |
- | 2 | cardName:鏍″洯缃 |
- | 1 | 校园网 |
- +------+-----------------------+
- 3 rows in set (0.00 sec)
插入成功,上面说明和中文没有关系,其实MySQL内部会进行转换,所以问题不在这里。既然问题在第2个insert,那直接查看下是否乱码:
- jinyizhou@localhost : test 06:04:14>select concat('cardName:校园网',char(59),'offlineCardType:campus');
- +----------------------------------------------------------------+
- | concat('cardName:校园网',char(59),'offlineCardType:campus') |
- +----------------------------------------------------------------+
- | cardName:校园网;offlineCardType:campus |
- +----------------------------------------------------------------+
显示没有问题,没有乱码,但是写入却出现乱码。这里注意到了一个函数:char(56),我们把这个函数转成字符串看看:
- jinyizhou@localhost : test 06:10:09>insert into abc values(2,concat('cardName:校园网',';','offlineCardType:campus'));
- Query OK, 1 row affected (0.00 sec)
- jinyizhou@localhost : test 06:14:49>select * from abc;
- +------+-------------------------------------------+
- | id | c |
- +------+-------------------------------------------+
- | 1 | 我们 |
- | 2 | cardName:鏍″洯缃 |
- | 1 | 校园网 |
- | 2 | cardName:校园网;offlineCardType:campus |
- +------+-------------------------------------------+
到此问题找到了,原来是char()函数的问题。那就在手册里看看char函数的相关知识:
- CHAR() returns a binary string. To produce a string in a given character set, use the optional USING clause:
#char()返回的是一个二进制字符串,可选择使用USING语句产生一个给出的字符集中的字符串:
mysql> SELECT CHARSET(CHAR(0x65)), CHARSET(CHAR(0x65 USING utf8));- +---------------------+--------------------------------+
- | CHARSET(CHAR(0x65)) | CHARSET(CHAR(0x65 USING utf8)) |
- +---------------------+--------------------------------+
- | binary | utf8 |
- +---------------------+--------------------------------+
按照上面的提醒,使用using utf8 给出一个字符串,即把二进制转换成了字符串。指定字符集后再次执行:
- jinyizhou@localhost : test 06:14:52>insert into abc values(2,concat('cardName:校园网',char(59 using utf8),'offlineCardType:campus'));
- Query OK, 1 row affected (0.00 sec)
- jinyizhou@localhost : test 06:22:24>select * from abc;
- +------+-------------------------------------------+
- | id | c |
- +------+-------------------------------------------+
- | 1 | 我们 |
- | 2 | cardName:鏍″洯缃 |
- | 1 | 校园网 |
- | 2 | cardName:校园网;offlineCardType:campus |
- | 2 | cardName:校园网;offlineCardType:campus |
- +------+-------------------------------------------+
看到成功插入,问题现在很明显了,就是出在char()函数上面?再推敲下:
- jinyizhou@localhost : test 09:38:17>insert into abc values(2,char(59));
- Query OK, 1 row affected (0.00 sec)
- jinyizhou@localhost : test 09:38:50>select * from abc;
- +------+-------------------------------------------+
- | id | c |
- +------+-------------------------------------------+
- | 1 | 我们 |
- | 2 | cardName:鏍″洯缃 |
- | 1 | 校园网 |
- | 2 | cardName:校园网;offlineCardType:campus |
- | 2 | cardName:校园网;offlineCardType:campus |
- | 2 | ; |
- +------+-------------------------------------------+
问题要是出在char()上面的话,上面的应该报错,那为什么没问题呢,再看看和concat一起用:
- jinyizhou@localhost : test 09:38:53>insert into abc values(2,concat('我',char(59)));
- Query OK, 1 row affected, 1 warning (0.00 sec)
- jinyizhou@localhost : test 09:39:38>select * from abc;
- +------+-------------------------------------------+
- | id | c |
- +------+-------------------------------------------+
- | 1 | 我们 |
- | 2 | cardName:鏍″洯缃 |
- | 1 | 校园网 |
- | 2 | cardName:校园网;offlineCardType:campus |
- | 2 | cardName:校园网;offlineCardType:campus |
- | 2 | ; |
- | 2 | 鎴 |
- +------+-------------------------------------------+
这里就看出来问题所在了,最终问题定位在concat+char上面。
上面已经知道char()在官方的说明了,继续在手册里看看concat函数的相关知识:
- If the arguments include any binary strings, the result is a binary string. A numeric argument is converted to its equivalent string form. to avoid that and produce a nonbinary string, you can use an explicit type cast, as in this example:
- SELECT CONCAT(CAST(int_col AS CHAR), char_col);
- 如果所有参数均为非二进制字符串,则结果为非二进制字符串。 如果自变量中含有任一二进制字符串,则结果为一个二进制字符串。一个数字参数被转化为与之相等的二进制字符串格式;若要避免这种情况,可使用显式类型 cast, 例如: SELECT CONCAT(CAST(int_col AS CHAR), char_col)
用charset查看类型:
- jinyizhou@localhost : test 10:58:21>select charset(concat('cardName:校园网',char(56),'offlineCardType:campus'));
- +------------------------------------------------------------------------+
- | charset(concat('cardName:校园网',char(56),'offlineCardType:campus')) |
- +------------------------------------------------------------------------+
- | binary |
- +------------------------------------------------------------------------+
- 1 row in set (0.00 sec)
按照上面的提醒,使用cast给出一个字符串,即把二进制转换成了字符串:
- jinyizhou@localhost : test 10:18:43>insert into abc values(2,concat('cardName:校园网',cast(char(59) as char),'offlineCardType:campus'));
- Query OK, 1 row affected (0.00 sec)
- jinyizhou@localhost : test 10:19:13>select * from abc;
- +------+-------------------------------------------+
- | id | c |
- +------+-------------------------------------------+
- | 1 | 我们 |
- | 2 | cardName:鏍″洯缃 |
- | 1 | 校园网 |
- | 2 | cardName:校园网;offlineCardType:campus |
- | 2 | cardName:校园网;offlineCardType:campus |
- | 2 | ; |
- | 2 | 鎴 |
- | 2 | cardName:校园网;offlineCardType:campus |
- +------+-------------------------------------------+
看到成功插入。说到这里大家都知道原因了:
char()函数返回的是一个二进制,concat()函数里只要有一个参数是二进制,则结果就是一个二进制。由于把一个二进制的值转成十六进制再写入到表,所以在插入的时候就会出现乱码,为什么不报错呢?因为转换出来的编码在字符集里找不到,虽然不报错,但会变成乱码。乱码的原因是:char()函数返回的是一个binary类型字符串,在进行concat时,会导致'cardName:校园网'字符串到binary的转换,整个结果返回是一个binary。UTF8字符集的三个汉字“校园网”占了9个字节。由于目标表字符集是GBK,因此在入库时,还会发生一次binary到GBK的转码,此时由于utf8和gbk字符集的范围不一样导致转换出错。要是表的字符集是utf8,则不会报错。
解决:
1)直接把char函数用其转换好的结果表示:如用";"来代替char(56)。
2)直接转化成字符串:char(59 using utf8) 来替换char(56)。
3)直接转化成字符串:cast(char(59) as char) 来代替char(56)。
4)直接把表的字符集改成utf8。
总结:
MySQL乱码的问题有很多情况,大致的原理可以看 http://www.cnblogs.com/zhoujinyi/p/4618887.html。总的一句话就是保证客户端,服务端和表的字符集一致,尽量避免不一致引发的问题。
参考:
http://www.cnblogs.com/cchust/p/4601536.html
http://www.cnblogs.com/zhoujinyi/p/4618887.html
MySQL字符集转换引发插入乱码问题的更多相关文章
- MySQL字符集转换(latin1到utf8)
http://blog.chinaunix.net/uid-25266990-id-3344584.html
- Mysql字符集知识总结
字符集&字符编码方式 字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,这里的字符可以是英文字符,汉字字符,或者其他国家语言字符. 常见字符集 ...
- mysql已有数据字符集转换
下面模拟把latin1字符集的数据转换为utf8字符集 一.创建测试表和测试数据: 1.修改会话级别的连接字符集 mysql > set names latin1; 查看一下: 2.创建测试表: ...
- MySQL字符集设置及字符转换(latin1转utf8)
MySQL字符集设置及字符转换(latin1转utf8) http://blog.chinaunix.net/uid-25266990-id-3344584.html MySQL字符集设置及字符转换 ...
- MySQL字符集 GBK、GB2312、UTF8区别 解决 MYSQL中文乱码问题 收藏 MySQL中涉及的几个字符集
MySQL中涉及的几个字符集 character-set-server/default-character-set:服务器字符集,默认情况下所采用的.character-set-database:数据 ...
- MySQL从删库到跑路(二)——MySQL字符集与乱码解析
作者:天山老妖S 链接:http://blog.51cto.com/9291927 一.字符集与编码 1.字符集简介 字符(Character)是各种文字和符号的总称,包括各国家文字.标点符号.图形符 ...
- [转]关于Navicat和MYSQL字符集不统一出现的中文乱码问题
原文链接:关于Navicat和MYSQL字符集不统一出现的中文乱码问题 最近遇到一串关于MYSQL中文乱码的问题,问题背景是这样的: 在此之前,服务器上安装好MySQL之后就立马重新配置了字符集为ut ...
- mysql5.5字符集设置的一点变化(对于中文乱码问题,需要设置mysql字符集)
工作中因为字符集问题没少头疼,还犯过一次错误,还好拯救及时,没有发生重大事故,唉,弄清楚点还是非常有必要的: 例如我的工作环境为CTR+redhat5+mysql5.5 在导入sql语句的时候必须要注 ...
- Python datatime 格式转换,插入MySQL数据库
Python datatime 格式转换,插入MySQL数据库 zoerywzhou@163.com http://www.cnblogs.com/swje/ 作者:Zhouwan 2017-11-2 ...
随机推荐
- 【8-18】JS学习01
Source : http://www.w3school.com.cn/html 外部的 JavaScript 也可以把脚本保存到外部文件中.外部文件通常包含被多个网页使用的代码. 外部 JavaSc ...
- XHTML的若干注意点
1.重要的兼容性提示: 你应该在 "/" 符号前添加一个额外的空格,以使你的 XHTML 与当今的浏览器相兼容. 2.XML对大小写敏感. 3.在 XHTML 中是不允许使用空标签 ...
- 用jquery写循环播放div的相关笔记 珍贵的总结 -1
用jquery写循环播放div line-height应用的元素的 层次? line-heig字ht, 叫行高, 仅仅是指 文/文本, 而不管图片. line-height是容器中 文本行 与 文本行 ...
- hdu4950 Monster (水题)
4950 Monster Monster Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...
- Java当中的内存分配以及值传递问题内存解析
首先必须说明作为Java程序员对于内存只要有大致的了解就可以了,如果你对Java当中的某一个知识点在不需要分析内存分配过程的情况下可以掌握,那就大可不必去研究内存.如果你对知识点已经掌握,那么你应该把 ...
- 使用MVVM框架avalon.js实现一个简易日历
最近在做公司内部的运营管理系统,因为与日历密切相关,同时无需触发条件直接显示在页面上,所以针对这样的功能场景,我就用avalon快速实现了一个简易日历,毕竟也是第一次造日历这种轮子,所以这里记录下我当 ...
- 第二章平稳时间序列模型——AR(p),MA(q),ARMA(p,q)模型及其平稳性
1白噪声过程: 零均值,同方差,无自相关(协方差为0) 以后我们遇到的efshow如果不特殊说明,就是白噪声过程. 对于正态分布而言,不相关即可推出独立,所以如果该白噪声如果服从正态分布,则其还将 ...
- FineUI第十一天---布局概述
布局概述 1.填充整个页面: 让整个容器填充整个页面,设置PageManager的AutoSizePanelID为需要填充整个页面的容器控件ID. 2. 填充整个容器(Fit): 让一个控件填满另一个 ...
- javaweb servlet中使用请求转发乱码
乱码的方式有很多,这里指出一种不容易想到的 *请确保您的页面单独访问正常,经过servlet请求转发时,有PrintWriter out = response.getWriter()不正常,没有正常 ...
- 锋利的jQuery书中推荐的几款插件
1.jQuery表单验证插件——Validation 2.jQuery表单插件——Form 3.模态窗口插件——SimpleModal 4.管理Cookie的插件——Cookie 5.jQuery U ...