MySQL 保存 emoji 表情失败报错

更新日期: 2018-01-02 阅读次数: 11006 分类: MySQL

有朋友在博客评论里反馈,无法提交带 emoji 表情的评论。第一反应是数据库编码问题。

由于一直不习惯使用 emoji,所以这个问题一直没发现。

Laravel 的报错信息

production.ERROR: SQLSTATE[HY000]: General error: 1267 Illegal mix of collations (utf8_unicode_ci,IMPLICIT) and (utf8mb4_unicode_ci,COERCIBLE) for operation '='

MySQL CHARSET 与 COLLATE 的区别

https://stackoverflow.com/questions/341273/what-does-character-set-and-collation-mean-exactly

  • CHARSET 即字符集编码。例如,一个字符集只有两个字符,假设 A 用 1 来编码,B 用 2 来编码。
  • COLLATE 即对比方法。用于指定数据集如何排序,以及字符串的比对规则。例如,ci,即大小写不敏感。

所以,当你看 wordpress 的建表语句时,会发现既指定了 CHARSET,也指定了 COLLATE。

而对于具体字段又单独指定了 COLLATE,我猜测是为了保证排序及比对的规则。

CREATE TABLE `wp_posts` (
  `post_title` text COLLATE utf8mb4_unicode_520_ci NOT NULL,
  ...
) ENGINE=InnoDB AUTO_INCREMENT=324 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;

如何查看当前数据库 CHARSET 及 COLLATE

mysql> SELECT default_character_set_name FROM information_schema.SCHEMATA 
    -> WHERE schema_name = "db_2018";
+----------------------------+
| default_character_set_name |
+----------------------------+
| latin1                     |
+----------------------------+
1 row in set (0.00 sec)

没想到我居然用的是 latin1 ...

查看当前数据表 CHARSET 及 COLLATE

mysql> SELECT CCSA.character_set_name FROM information_schema.`TABLES` T,
    ->        information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` CCSA
    -> WHERE CCSA.collation_name = T.table_collation
    ->   AND T.table_schema = "db_2018"
    ->   AND T.table_name = "products";
+--------------------+
| character_set_name |
+--------------------+
| utf8               |
+--------------------+
1 row in set (0.01 sec)

果然,数据表使用了 utf8 charset,而不是 utf8mb4。

但是这样查看,非常的不直观,因为我还想同时看到每列的设置,所以,远不如

mysql> show create table products;

CREATE TABLE `products` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
  `desc` text COLLATE utf8_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=615 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

如何修改数据表及字段的编码

修改数据表的编码之后,会默认将字段一并修改。执行结果如下

mysql> ALTER TABLE products CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
mysql> show create table products;

CREATE TABLE `products` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL,
  `desc` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=615 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

如何修改数据库的编码

虽然改了数据表就不需要再改数据库的编码了,但是还是统一一下比较好。

ALTER DATABASE db_2018 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Laravel 的默认编码

打开 laravel 5.5 config/database.php 文件,会发现默认的 mysql 编码就是 utf8mb4。

Laravel 好样的。。。

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式