V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
XIVN1987
V2EX  ›  Python

PyMySQL 插入二进制数

  •  
  •   XIVN1987 · 2016-12-01 10:28:45 +08:00 · 3968 次点击
    这是一个创建于 2921 天前的主题,其中的信息可能已经有所发展或是发生改变。
    PyMySQL 执行 SQL 语句的函数如下:
    def query(self, sql, unbuffered=False):
    if isinstance(sql, text_type) and not (JYTHON or IRONPYTHON):
    if PY2:
    sql = sql.encode(self.encoding)
    else:
    sql = sql.encode(self.encoding, 'surrogateescape')
    self._execute_command(COMMAND.COM_QUERY, sql)

    PyMySQL 会把它要执行的 SQL 语句都执行一下 encode ,,这样我插入二进制数据时 SQL 语句中的二进制数据字节也会因此而被修改,插入数据库的数据显然就不是我要插入的数据了,,请问这种情况怎么办?? PyMySQL 没有办法控制不对要执行的 SQL 语句 encode 吗?
    21 条回复    2016-12-01 22:59:42 +08:00
    mhycy
        1
    mhycy  
       2016-12-01 10:29:55 +08:00   ❤️ 1
    BASE64 编码后插入
    XIVN1987
        2
    XIVN1987  
    OP
       2016-12-01 10:57:44 +08:00
    @mhycy
    多谢,这种方法确实可行,可是却增加了 1/4 的数据量,而且存入和取出时还需要编解码,所以我还是希望能够找到直接存入二进制数据的方法
    mhycy
        3
    mhycy  
       2016-12-01 11:08:30 +08:00
    @XIVN1987
    刚刚看了下源码,无解
    问题不仅仅在 query 在 query 之前的参数化拼接也有转码

    可以尝试直接调用_execute_command 这个“私有”函数

    PS. 同时获知这货不支持参数化查询
    XIVN1987
        4
    XIVN1987  
    OP
       2016-12-01 11:37:25 +08:00
    @mhycy
    直接调用_execute_command 我试过了,报错“命令不同步”,所以绕过编码直接调用底层函数可能要写很多代码才行,,所以这种方法不可行

    不过我倒发现另外一个可行的方法,就是建立连接的时候 connect 函数参数 charset 设置为''而非'uft-8',这样就算执行编码语句也不会改变数据,,可问题是如果这样的话其他需要编码的数据字段就得全部手动执行编码,非常麻烦

    所以我又想到一个另类的方法,建立两个 connect ,,一个 charset 为'utf-8'用来执行无二进制数据的 SQL 语句,一个 charset 为''专门执行有二进制数据的 SQL 语句

    不过这也太不优雅了,,我想 PyMySQL 不至于只能这么做吧
    XIVN1987
        5
    XIVN1987  
    OP
       2016-12-01 11:44:02 +08:00
    @mhycy
    我想我找到方法了:
    在插入二进制数据之前调用 con.set_charset('lati-1'),这样编码就不会改变数据
    在插入二进制数据之后调用 con.set_charset('utf-8'),
    lerry
        6
    lerry  
       2016-12-01 11:46:45 +08:00
    楼主看这个
    https://github.com/PyMySQL/PyMySQL/blob/master/pymysql/tests/test_basic.py

    "binary\x00data".encode(conn.charset)

    PyMySQL 肯定是能处理好二进制的,这是个基本需求嘛
    mhycy
        7
    mhycy  
       2016-12-01 11:57:42 +08:00
    @XIVN1987

    你这是取巧办法啊
    每次都调用一次 encode 让人不放心..囧

    @lerry 依赖底层的编码处理,能用,但不靠谱
    mhycy
        8
    mhycy  
       2016-12-01 12:00:20 +08:00
    补充:
    我认为还是趁早放弃在 MySQL 放二进制文件比较靠谱,要是数据小的话 base64 也不会增加太多空间消耗。
    从可靠性来看是更优选。
    XIVN1987
        9
    XIVN1987  
    OP
       2016-12-01 12:31:04 +08:00
    @lerry
    比如二进制数'\xF0\xA0'放到 SQL 语句中它是
    "INSERT INTO ........... '\xF0\xA0' ......"
    可是 PyMySQL 在执行这条 SQL 语句前会对这条语句执行 encode('utf-8'),语句就变成了
    "INSERT INTO ........... '\xF0\xC2\xA0' ......"
    看到没,,数据变了!!!这样 2 个字节的数据插入到 MySQL 里面就变成了 3 个字节!!!
    sujin190
        10
    sujin190  
       2016-12-01 12:44:28 +08:00   ❤️ 1
    其实你再看一下源码,其中是只有你传过去的 sql 是 unicode 的时候才编码的,你可以自己拼接 sql ,然后编码成 utf-8 字符串传进去就不会再进行编码了
    sujin190
        11
    sujin190  
       2016-12-01 12:45:51 +08:00
    而且如果你发送的 sql 是 unicode ,为了能在网络上发送,编码成二进制数据是很正常的啊,并没有什么问题
    XIVN1987
        12
    XIVN1987  
    OP
       2016-12-01 12:55:41 +08:00
    @sujin190
    多谢指点,如你所说,确实只有当 sql 是 unicode ( Py3 下的 str )时才会执行 encode('utf-8'),所以在执行 sql 前执行一下编码变成 bytes 就不会在被编码了,,不过不能编码成 utf-8 再传,而是用 latin-1 编码再传
    TaMud
        13
    TaMud  
       2016-12-01 15:06:23 +08:00
    execute

    满足你
    mhycy
        14
    mhycy  
       2016-12-01 15:34:59 +08:00
    @TaMud

    别上来就乱答,看过源码再说话,从 execute 开始到最终 query 好几层 encode 在上面
    TaMud
        15
    TaMud  
       2016-12-01 17:22:49 +08:00
    我错了,我不该帮忙的
    你折腾你快乐,可惜没脑子
    TaMud
        16
    TaMud  
       2016-12-01 17:25:03 +08:00
    我以后要记得教训,不能帮智障,不然还会被骂

    我不由想起一句话:脑子是个好东西,可惜你没有
    TaMud
        17
    TaMud  
       2016-12-01 17:26:33 +08:00
    我还不由想起一个故事
    A:师傅,你的刀不行呀,你看,这个骨头砍了半天砍不断
    B:你刀拿反了
    TaMud
        18
    TaMud  
       2016-12-01 17:28:30 +08:00
    A:师傅,你不懂不要瞎说,刀不是砍骨头的嘛
    B:你刀拿反了
    A:师傅,是不是你的刀是伪劣产品
    B:你刀拿反了

    我一笑而过
    XIVN1987
        19
    XIVN1987  
    OP
       2016-12-01 17:35:23 +08:00
    @TaMud
    知之为之啊少年,人家好心给你指出错误,你不感谢也就罢了,竟然还嘲讽。。。

    cursor.execute()最终也是要调用 connection.query()的
    TaMud
        20
    TaMud  
       2016-12-01 17:40:41 +08:00
    你刀拿反了
    billlee
        21
    billlee  
       2016-12-01 22:59:42 +08:00
    自己拼接 SQL 语句,把数据转换成 0x0123456789abcdef 这样的十六进制字面值应该是可以的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   922 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 20:55 · PVG 04:55 · LAX 12:55 · JFK 15:55
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.