问题背景

有同事反馈在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)实际是字符“;”,为了验证想法,做了两个实验:

  1. 将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的。

问题产生的两个关键点

  1. 连接字符集与表字符集不匹配
  2. 使用了char函数

解决办法

1.char函数提供了using语法来实现返回特定字符集的字符串,比如:char(59 using utf8)

2.保证连接字符集与表字符集一致。

一条诡异的insert语句的更多相关文章

  1. 24_MySQL插入insert语句

    本节涉及SQL语句: -- MYSQL 基础操作 1.插入insert语句 INSERT INTO t_dept(deptno,dname,loc) VALUES(70,"后勤部" ...

  2. 一条insert语句批量插入多条记录

    一条insert语句批量插入多条记录 常见的insert语句,向数据库中,一条语句只能插入一条数据: insert into persons (id_p, lastname , firstName,  ...

  3. laravel 获取上一条insert语句产生的id

    <?php //頭部引入DB類 use Illuminate\Support\Facades\DB; //在方法中獲取获取上一条insert语句产生的id $id = DB::getPdo()- ...

  4. 跟随一条insert语句, 进入TiDB的源码世界(上)

    TiDB是Google F1的开源实现: TiDB实现了基于mvcc的乐观锁,在线表结构变更,基于时间戳的数据线性一致性,等等: 为了可靠性,TiDB和Oracle一样,维护了百万级别的自动化测试用例 ...

  5. Sql Server系列:Insert语句

    1 INSERT语法 [ WITH <common_table_expression> [ ,...n ] ] INSERT { [ TOP ( expression ) [ PERCEN ...

  6. 如何判断一条sql(update,delete)语句是否执行成功

    如何判断一条sql(update,delete)语句是否执行成功 catch  (SQLException    e)  {  }  catch不到错误应该就成功了.   ============== ...

  7. sql插入多条数据的sql语句

    sql插入多条数据的sql语句 有三种方法:1.InSert Into <表名>(列名)Select <列名>From <源表名>如:INSERT INTO Ton ...

  8. SQL Insert语句数据以以unicode码存储 解决存储数据出现乱码的问题

    写了个读取原始的文本数据导入数据库的工具 ,最后发现空中有几个值是乱码 例如 原始数据是 :Bjørn 存到数据库中是 Bj?rn 研究半天发现是一直以来忽略了一个标记‘N’ 2条 Insert 语句 ...

  9. 老李分享:MySql的insert语句的性能优化方案

    老李分享:MySql的insert语句的性能优化方案   性能优化一直是测试人员比较感兴趣的内容,poptest在培训学员的时候也加大了性能测试调优的方面的内容,而性能优化需要经验的积累,经验的积累依 ...

随机推荐

  1. Scalaz(18)- Monad: ReaderWriterState-可以是一种简单的编程语言

    说道FP,我们马上会联想到Monad.我们说过Monad的代表函数flatMap可以把两个运算F[A],F[B]连续起来,这样就可以从程序的意义上形成一种串型的流程(workflow).更直白的讲法是 ...

  2. 泛函编程(35)-泛函Stream IO:IO处理过程-IO Process

    IO处理可以说是计算机技术的核心.不是吗?使用计算机的目的就是希望它对输入数据进行运算后向我们输出计算结果.所谓Stream IO简单来说就是对一串按序相同类型的输入数据进行处理后输出计算结果.输入数 ...

  3. 泛函编程(5)-数据结构(Functional Data Structures)

    编程即是编制对数据进行运算的过程.特殊的运算必须用特定的数据结构来支持有效运算.如果没有数据结构的支持,我们就只能为每条数据申明一个内存地址了,然后使用这些地址来操作这些数据,也就是我们熟悉的申明变量 ...

  4. SDRAM,DRAM,SRAM,DDR的概念

    一:SDRAM SDRAM(Synchronous Dynamic Random Access Memory),同步动态随机存储器,同步是指 Memory工作需要同步时钟,内部的命令的发送与数据的传输 ...

  5. Java经典实例:正则表达式,找到匹配的文本

    import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created by Frank */ public cla ...

  6. 通过rsync+inotify实现数据实时备份同步

    一.环境描述 测试环境 需求:服务器A与服务器B为主备服务模式,需要保持文件一致性,现采用sersync基于rsync+inotify实现数据实时同步 环境描述: 主服务器172.26.7.50 ,从 ...

  7. 获取spring容器要小心的坑

    情景: 今天想把一个web应用中的上传路劲更改一下 于是就在配置文件作了如下变动: <bean id="uploadServelt" class="com.xxxx ...

  8. Hover States - 有趣的用户界面及交互设计

    Hover States 一组新潮的和有趣的用户界面和交互设计的集合.Hover States 的目标是要成为设计师和开发人员灵感来源,向人们展示目前人们正在做的各种网站中令人惊奇的效果.他们认为交互 ...

  9. CSS基础教程 -- 媒体查询屏幕适配

    响应式布局 Media Query 的使用方法 在上例中, 我们使用Media Queries来根据3种不同尺寸的窗口使用3种不同的样式.通过不同的媒体类型和条件定义样式表规则,媒体查询让CSS可以更 ...

  10. ng-cordova 手机拍照或从相册选择图片

    1.需求描述 实现一个调用摄像头拍照,或者直接打开本地图库选择照片,然后替换App中图片的功能 2.准备 1) 安装ng-cordova 进入到ionic工程目录,使用bower工具安装, bower ...