【问题标题】:mysql-python collation issue: how to force unicode datatype?mysql-python 排序规则问题:如何强制使用 unicode 数据类型?
【发布时间】:2012-03-01 19:12:04
【问题描述】:

出于某些目的,我不得不将数据库中的字段排序规则从 utf8_unicode_ci 更改为 utf8_bin。事实证明,这种变化导致了 Python 数据类型的变化。

问题是如何强制mysql-python将unicode对象返回给python

这是一个显示问题的示例(显式字符集强制 use_unicode=1):

>>> con = MySQLdb.connect(..., charset='utf8')
>>> c = c.cursor()
>>> c.execute('SELECT %s COLLATE utf8_bin', u'м')
1L
>>> c.fetchone()
('\xd0\xbc',)
>>> c.description
(("'\xd0\xbc' COLLATE utf8_bin", 253, 2, 3, 3, 31, 0),)


>>> c.execute('SELECT %s COLLATE utf8_unicode_ci', u'м')
1L
>>> c.fetchone()
(u'\u043c',)
>>> c.description
(("'\xd0\xbc' COLLATE utf8_unicode_ci", 253, 2, 3, 3, 31, 0),)

在我的数据库中,字段的类型是 VARCHAR,但更改后它们的行为类似于 BINARY,这不是我想要的。

【问题讨论】:

  • @Robus, charset=utf8 完全一样
  • 不确定,因此我只将其发布为评论 :) 我相信实际上“解码”变量 python 端可能比弄乱 mysql 需要更少的努力
  • @Robus,我想,问题其实不在于mysql,而在于mysql-python。我确实想知道它如何区分两种情况。光标描述相同。

标签: python mysql mysql-python


【解决方案1】:

事实证明,这个问题相当尴尬。简而言之,MySQL string datatypes 中的大多数变体和物种都映射到 MySQL 接口中的单个数据类型,并带有一个额外的 BINARY 标志。

因此,MySQL 的 VARCHARVARBINARY 和字符串文字映射到列类型定义中的相同 MySQLdb.constants.FIELD_TYPE.VAR_STRING 类型,但当类型为 VARBINARY 或经过排序的字符串时,有一个额外的 MySQLdb.constants.FLAG.BINARY 标志使用*_bin 排序规则。

虽然有MySQLdb.constants.FIELD_TYPE.VARCHAR类型,但是我没查到什么时候用的。正如我所说,MySQL VARCHAR 列映射到 FIELD_TYPE.VAR_STRING

如果您的应用程序使用真正的二进制字符串(例如,您存储图像并使用与文本相同的连接获取它们),该解决方案将变得相当脆弱,因为它假定将所有二进制字符串解码为 un​​icode。不过,它确实有效。

正如官方docs 所说:

因为 MySQL 将所有数据作为字符串返回,并希望您自己转换。这将是一个真正的痛苦,但事实上,_mysql 可以为你做到这一点。 (而 MySQLdb 会为您执行此操作。)要完成自动类型转换,您需要创建一个类型转换器字典,并将其作为 conv 关键字参数传递给 connect()。

在实践中,真正令人头疼的可能是构建您自己的转换器字典的过程。但是您可以从MySQLdb.converters.conversions 导入默认值并对其进行修补,甚至可以在连接实例上对其进行修补。诀窍是删除FLAG.BINARY 标志的特殊转换器并为所有情况添加解码器。如果您为MySQLdb.connect 显式指定charset 参数,它会强制使用use_unicode=1 参数,这会为您添加解码器,但您可以自己做:

>>> con = MySQLdb.connect(**params)
>>> con.converter[FIELD_TYPE.VAR_STRING]
[(128, <type 'str'>), (None, <function string_decoder at 0x01FFA130>)]
>>> con.converter[FIELD_TYPE.VAR_STRING] = [(None, con.string_decoder)]
>>> c = con.cursor()
>>> c.execute("SELECT %s COLLATE utf8_bin", u'м')
1L
>>> c.fetchone()
(u'\u043c',)

如果需要,您可能需要对FIELD_TYPE.STRING 进行相同的修改。

另一种解决方案是将显式 use_unicode=0 传递给 MySQLdb.connect 并在您的代码中进行所有解码,但我不会。

希望,这可能对某人有用。

【讨论】:

    【解决方案2】:

    在低级别使用 Mysql-Python 有很大的变化,但我认为更好的主意是使用 sqlalchemy 之类的东西,而不是直接使用 db-api,然后你可以使用例如types.Unicode 并知道它正在执行 db-api 的 unicode 支持所需的操作

    在你因为我没有直接回答问题而责怪我之前,请考虑一下:mysql-python aka MySQLdb 只是 MySQL 的几个 db-api 之一。新版本可能会继续支持 MySQLdb,但在某些情况下(例如迁移到 python 3x,或者您无法安装二进制模块的主机)可能会迫使您在未来使用其他东西,例如如oursqlmyconnpy。制作 sqlalchemy 的人已经付出了很多努力来支持多个 db-api,并且在 mysql-python 的情况下,过去甚至解决了严重的错误。使用 sqlalchemy,更改为不同的 db-api 就像更改连接 URL 一样简单,并且它可以确保数据类型强制的任何内容都按照您的预期进行处理。

    也就是说,要利用它,您需要根据 sqlalchemy 的模式定义表并使用它们的查询 API,但您会得到很多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-03
      • 2011-08-28
      • 1970-01-01
      • 2018-05-12
      • 2017-06-19
      • 2020-05-26
      • 1970-01-01
      • 2010-12-28
      相关资源
      最近更新 更多