一. 前言
本文详解记录了该错误产生的原因以及解决的过程,如果您只是为了快速解决本问题,请直接点击目录中的第六节
二. 数据准备
我们以5-1节的记录为例,首先创建一个数据库imooc:
CREATE DATABASE imooc; USE imooc;
然后创建一个数据表:
CREATE TABLE IF NOT EXISTS tdb_goods( goods_id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, goods_name VARCHAR(150) NOT NULL, goods_cate VARCHAR(40) NOT NULL, brand_name VARCHAR(40) NOT NULL, goods_price DECIMAL(15,3) UNSIGNED NOT NULL DEFAULT 0, is_show BOOLEAN NOT NULL DEFAULT 1, is_saleoff BOOLEAN NOT NULL DEFAULT 0 );
三. 插入记录
我们都知道,MySQL默认的编码为latin1,所以我们上面创建数据表的编码也是latin1:
mysql> show create table tdb_goods \G *************************** 1. row *************************** Table: tdb_goods Create Table: CREATE TABLE `tdb_goods` ( `goods_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, `goods_name` varchar(150) NOT NULL, `goods_cate` varchar(40) NOT NULL, `brand_name` varchar(40) NOT NULL, `goods_price` decimal(15,3) unsigned NOT NULL DEFAULT '0.000', `is_show` tinyint(1) NOT NULL DEFAULT '1', `is_saleoff` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`goods_id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1 1 row in set (0.00 sec)
在此基础上,我们插入一条记录:
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('R510VC 15.6英寸笔记本','笔记本','华硕','3399',DEFAULT,DEFAULT);
现在插入记录是可以正常插入的:
mysql> SELECT * FROM tdb_goods; +----------+-----------------------+------------+------------+-------------+---------+------------+ | goods_id | goods_name | goods_cate | brand_name | goods_price | is_show | is_saleoff | +----------+-----------------------+------------+------------+-------------+---------+------------+ | 1 | R510VC 15.6英寸笔记本 | 笔记本 | 华硕 | 3399.000 | 1 | 0 | +----------+-----------------------+------------+------------+-------------+---------+------------+ 1 row in set (0.00 sec)
可以看到记录中有中文,很多初学者往往会有疑问,编码为latin1,为什么还支持中文呢?
其实如果数据库内表的字符集是latin1,那么默认情况下中文也可被支持,latin1覆盖了所有单字节的值,任何其他的码流都可以被看做latin1,如果我们在创建数据库和访问数据库时如果都采用默认的latin1,那就不仅仅支持中文,而是支持任意的编码方式。
四. 错误产生
有了上面的基础后,我们来看一下该错误的产生经过:
我的一个小伙伴在查看我5-1:数据准备后,按照笔记中的要求,创建了数据库,和数据表,并输入了以下指令:
SET NAMES gbk;
然后开始插入记录,但是在插入的时候的时候,却出现了1366错误:
mysql> INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('Y400N 14.0英寸笔记本电脑','笔记本','联想','4899',DEFAULT,DEFAULT); ERROR 1366 (HY000): Incorrect string value: '\xD3\xA2\xB4\xE7\xB1\xCA...' for column 'goods_name' at row 1
刚遇到这个问题的时候,我和小伙伴都想到了可能是编码的问题,于是我们首先将my.ini的默认编码修改为utf8,然后正好需要关机,便没有重启MySQL服务,然后修改了数据表的编码:
ALTER TABLE 表名 DEFAULT CHARACTER SET utf8;
却发现有三个字段都变成了latin1编码:
mysql> show create table tdb_goods \G *************************** 1. row *************************** Table: tdb_goods Create Table: CREATE TABLE `tdb_goods` ( `goods_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, `goods_name` varchar(150) CHARACTER SET latin1 NOT NULL, `goods_cate` varchar(40) CHARACTER SET latin1 NOT NULL, `brand_name` varchar(40) CHARACTER SET latin1 NOT NULL, `goods_price` decimal(15,3) unsigned NOT NULL DEFAULT '0.000', `is_show` tinyint(1) NOT NULL DEFAULT '1', `is_saleoff` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`goods_id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 1 row in set (0.00 sec)
接着我们又修改字段的编码:
ALTER TABLE 表名 CHANGE 字段名 字段名 数据类型 CHARACTER SET utf8 NOT NULL;
发现还是不行……接着又新创建了一个数据库,发现并不是utf8编码,依旧是latin1编码,这时才想起来没有重启MySQL服务,然后重启了MySQL服务(方法请查看1-3:启动与停止MySQL服务)后,发现还是不行,最后只能重新创建了一个数据库,勉强跳过了这个问题
五. SET NAMES XXX;
那么产生该错误的根本原因是什么呢,我们首先来详解一下SET NAMES XXX命令:
这个命令设置的是客户端发出命令的编码、连接层的编码以及服务端返回结果的编码,相当于是客户端和服务器交互用的编码,而不是数据保存的编码,也就是只影响客户端的显示方式,不影响数据表实际的数据,我们来举例说明:
首先我们查看一下当前联接系统参数:
mysql> show variables like 'char%' -> ; +--------------------------+---------------------------------------------------------+ | Variable_name | Value | +--------------------------+---------------------------------------------------------+ | character_set_client | latin1 | | character_set_connection | latin1 | | character_set_database | latin1 | | character_set_filesystem | binary | | character_set_results | latin1 | | character_set_server | latin1 | | character_set_system | utf8 | | character_sets_dir | C:\Program Files\MySQL\MySQL Server 5.5\share\charsets\ | +--------------------------+---------------------------------------------------------+ 8 rows in set (0.00 sec)
执行SET NAMES gbk; 命令后,再来查看一下当前连接系统参数:
mysql> SET NAMES GBK; Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'char%'; +--------------------------+---------------------------------------------------------+ | Variable_name | Value | +--------------------------+---------------------------------------------------------+ | character_set_client | gbk | | character_set_connection | gbk | | character_set_database | latin1 | | character_set_filesystem | binary | | character_set_results | gbk | | character_set_server | latin1 | | character_set_system | utf8 | | character_sets_dir | C:\Program Files\MySQL\MySQL Server 5.5\share\charsets\ | +--------------------------+---------------------------------------------------------+ 8 rows in set (0.00 sec)
可以看到,受影响的只有客户端的三条记录: character_set_client、character_set_connection、character_set_results,而真正服务器端的编码字符集,character_set_server 和 character_set_database ,set names 'xxx' 根本无法没有收到影响。
那么在5-1节中,当时为什么要输入这条指令呢?因为我在教程刚开始的时候,就在my.ini中将MySQL的默认编码方式为utf-8,所以这里需要在客户端以gbk的编码显示数据防止乱码,只影响客户端的显示方式,不影响数据表实际的数据
而我的小伙伴刚开始的时候没有将默认的编码方式设置为utf-8,仍然采用latin1,但是他又输入了SET NAMES gbk;命令,所以导致出现了1366错误
六. 根本解决方法
根本的解决方法其实很简单:
如果MySQL默认编码为latin1,则不要执行SET NAMES gbk;
如果MySQL默认编码为utf8,则执行SET NAMES gbk;命令后再插入记录
另外值得注意的是:
修改my.ini一定要记得重启MySQL服务
通过my.ini更改MySQL默认编码后,如果不重启MySQL服务,则新创建的数据库依旧是更改前的MySQL编码
如果重启服务,新创建的数据库才会变成更改后的MySQL编码,但是之前创建的数据库依旧是是更改前的MySQL编码
请登录之后再进行评论