MySQL 存储 Emoji

2019-12-23

This article is a bit old and the content may be outdated, so please refer to it with caution and remember to check the latest official materials (such as documentation, etc.)

最近需要使用 SQLAlchemy 存弹幕的内容,但是遇到了存 emoji 的问题。

UPDATE: See https://docs.sqlalchemy.org/en/13/dialects/mysql.html#unicode for more info

utf8_bin?

一开始就套用存储中文姓名的那一套,使用utf8_bin的 collation,觉得 utf8 这种万能的东西直接用就行了。可谁知给我报错:

mysql.connector.errors.DatabaseError: 1366 (HY000): Incorrect string value: '\xE8\x86\x9C' for column 'text' at row 1
text

蛤?竟有如此操作?

utf8mb4_unicode_ci?

一波搜索之后就看到了使用utf8mb4_unicode_ci的 collation。于是就写:

text = db.Column(db.Unicode(256, collation='utf8mb4_unicode_ci'))
python

可惜并没有实质性效果

暴力二进制 #

utf8 解决不了,二进制存储总行了吧?

果然,存进去并没有问题。但是当要读取出来的时候又报了奇怪的错误:

TypeError: string argument without an encoding
python

明明是二进制怎么给我搞出来一个编码问题?

后期查阅资料发现,SQL 似乎有一个动态类型,每列的数据类型是建议值,并不强制。其结果就是我存一个二进制编码的字符串,他就真的以为是字符类型,并且数据库表里没有存编码,导致了问题出现。

疾病设计😠!!!

直接escape #

二进制也不行,全 escape 总可以了吧!

>>> text.encode('unicode_escape').decode()
>>> text.decode('unicode_escape').encode()
python

总算成功存储和读取了!

人人都说utf8mb4_unicode_ci #

为什么就是不行呢?

哦,还要改数据库 charset, collation

ALTER DATABASE databasename CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE tablename CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
sql

还要给 SQLAlchemy 加 ?charset=utf8mb4

Edit: 使用 SQLALCHEMY_ENGINE_OPTIONS.connect_args 不应该加 ?charset=utf8mb4,加完之后会优先认 ?charset=utf8mb4,导致 collation 默认 utf8mb4_0900_ai_ci (which is MySQL 8.0's default but not implemented in MySQL 5.7)

可是为什么还是不行?

mysql.connector.errors.DatabaseError: 1273 (HY000): Unknown collation: 'utf8mb4_0900_ai_ci'
python

我几几年用过这编码?

版本大坑 #

我找到了mysql-connector-python的官方文档:https://dev.mysql.com/doc/connector-python/en/connector-python-connectargs.html

collation 一栏中:

Argument NameDefaultDescription
collationutf8mb4_general_ai_ci (is utf8_general_ci in 2.xWhich MySQL collation to use. The 8.x default values are generated from the latest MySQL Server 8.0 defaults.

什么?8.0 的默认值?我们只有 5.7 呢!

虽然并不是utf8mb4_0900_ai_ci这种东西,我还是试着把链接数据库参数修改成utf8mb4_unicode_ci

SQLALCHEMY_ENGINE_OPTIONS:
    connect_args:
        collation: utf8mb4_unicode_ci
yaml

噫!成功了!

事实证明utf8mb4_0900_ai_ciutf8mb4_general_ai_ci应该是同义词一样的存在。

可是:

MySQL Connector/Python 8.0 is highly recommended for use with MySQL Server 8.0, 5.7, 5.6, and 5.5. Please upgrade to MySQL Connector/Python 8.0.

好一个recommended,就给我挖这种坑?版本问题害死人!

AllanChain
AllanChain
2020-03-19
SQLALCHEMY_ENGINE_OPTIONS:
    connect_args:
        charset: utf8mb4
        collation: utf8mb4_unicode_ci
yaml

这里指定编码。

Leave your comments and reactions on GitHub