mysql的charset和collate

mysql的charset

charset用于给数据库确定使用哪种编码方式进行编码
image_1ccpviqo3i3d1tbs1fd22p470h9.png-76.1kB


mysql的collate

collate叫做数据库的校验,就是一种对字符串进行比较的规则
image_1ccpvon1q16g6b0v16dh1gkuh4qm.png-90.6kB

stackoverflow上对charset和collate的解释:

A character set is a set of symbols and encodings. A collation is a set of rules for comparing characters in a character set. Let’s make the distinction clear with an example of an imaginary character set.


mysql中常用的编码和校验方式

###utf-8和unicode###
unicode是确定字符的编码值,但是具体怎么用多大的空间来存储室没有确定的,在计算机系统中,存储在硬件的使用试用unicode进行编码,读出来进行读写是使用utf-8进行编码的

###utf-8和ascii###
ascii编码是使用7位来进行编码的,utf-8是小的字符用一字节,多的用到2字节到4字节,当时utf-8用一字节进行编码的时候和ascii一样。

###utf-8和latin1###
Latin1是ISO-8859-1的别名,顾名思义,1这种编码方式可以编码拉丁字母,ISO-8859-1收录的字符除ASCII收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。如果用latin1来编码非拉丁字母的中文日文等就会出现乱码。latin1用一字节来存储一个字符。


mysql实践中的问题

问题阐述
mysql database的编码和校验方式会影响mysql连接器(比如我一直是用mysql.connector)在查询varchar的时候,将结果返回为bytearray的形式还是是正常的string类型。charset为utf-8时,varchar字段的查询结果返回类型为bytearray,虽然这个问题可以在直接使用mysql.conector的时候自己decode一下来解决,但是当mysql.connector配合sqlalchemy这样的orm一起使用的时候,问题就尤为明显,而且难以解决。在sqlalchemy配合mysql.connector中,query函数中传入varchar类型的字段作为查询的条件的时候,报错:typeerror:bytearray is not hashalbe。显示返回的查询类型是bytearray,并且这是不可哈希的类型,所以sqlalchemy的查询报错。

问题原因
这应该是mysql.connector 2版本自己的设计或者一个bug,因为详细可以看Connector/Python 2.0.0官方文档说明
image_1ccq0rujm13qo1ln46ulrl51c4d13.png-28.1kB
这里说明,connector版本1返回值的类型是正常的,python2返回时字符串,python3是bytes,但是到了connector版本2,无论哪个版本的python返回值一律都是bytearray

解决方案
据我观察

  • charset为utf-8,返回值类型为bytearray
  • charset为latin1,返回值类型为string
  • charset为gbk,返回值类型为string,而gbk可以对中文编码

所以得出上面所说的,数据库的charset可以影响connector的返回值类型,这很迷,谷歌搜到的解释很多事stackoverflow上的,对于这个问题也是众说纷纭,所以也不知道这算是一个bug还是connector本身的一个设计。上面这样解决也要考虑当存储非拉丁字符时会出现乱码的情况。

在创建database时确定它的charset和collate

CREATE DATABASE db_name CHARACTER SET latin1 COLLATE latin1_swedish_ci;

latin1_swedish_ci意为瑞典的一个拉丁字符比较方式,ci意为不计较大小写。

通过上面设置database的charset和collate的方式就可以让connector的varchar返回类型为string,sqlalchemy的query不报错了。


总结

在mysql.connector+sqlalchemy对mysql进行含有中文字段的row进行增删查找,可以设置数据库的charset为gbk。这样mysql.connector查询的返回值为string,sqlalchemy的query不会报错。

1
create database test2 DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci;

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from sqlalchemy import Column, String, Integer, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# 创建对象的基类:
Base = declarative_base()

# 定义User对象:
class User(Base):
__tablename__ = 'user'
id = Column(String(20), primary_key=True)
name = Column(String(20))

# 初始化数据库连接:
engine = create_engine('mysql+mysqlconnector://root:Gzm20125@localhost:3306/test')
# 创建DBSession类型:
DBSession = sessionmaker(bind=engine)

#Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
# 创建session对象:

session = DBSession()
# 创建新User对象:
new_user = User(id="5", name='Bob')
# 添加到session:
session.add(new_user)
# 提交即保存到数据库:
session.commit()
# 关闭session:
session.close()

# 创建Session:
session = DBSession()
# 创建Query查询,filter是where条件,最后调用one()返回唯一行,如果调用all()则返回所有行:
user = session.query(User).filter_by(id = "5").one()
# 打印类型和对象的name属性:
print('type:', type(user))
print('name:', user.name)
# 关闭Session:
session.close()

image_1ccq1cgd91bo6hl21le3fpo1d6v1g.png-8.3kB