——这个问题烦扰了我将近3个星期。
重要提醒:想看解决方案的直接拖到最后,这中间的过程,急性子的人可以不用看。
问题描述:
现有:
1. unicode字符串:a = "测试" ;
2. 数据库SQL Server 2008表 t(name nvarchar(100))
3. linux主机:CentOS6.7 64bit(run in docker)
4. 驱动及接口:unixODBC2.2.14、FreeTDS0.9.1、pyodbc3.0.10
想要把这个a字符串插入到t表中。
就这么简单。
实际上,这个并不简单,unicode字符串通过pyodbc插入到sqlserver里面的时候,会变成:
?μ?èˉ?
KÕ
之类的东东,俗称乱码。
问题的详细描述请看我在论坛的发帖:求助:Linux下使用Pyodbc插入数据到MSSQL中文乱码
只有一条回帖,而且没有啥价值(给点提示和意见都好啊~哭)。
简单的来说,就是,windows下能正常执行的代码,插入中文无压力,到了linux下,就不行了,插入的是乱码。
而且最怪异的是pyodbc用的是freetds的驱动,在freetds里面insert却毫无压力,中文正常到吐。
今晚找了个VPN,把StackOverFlow翻了个遍,终于找到了解决方案。
废话真多(你也觉得是吧)。
参考文献1的问题就和我的描述基本一致,只是,作者的数据库是latin1的env。
投票最多的那个答案嘛,给了我一个提示:
通过FreeTDS进行数据库连接,路径如下:
Python App --1-- pyodbc --2-- FreeTDS --3-- DBServer
而这每一步都有一个coding的问题在。
首先,保证python脚本的coding必须要正确,例如utf-8,这个可以在windows下写好脚本执行尝试一下,确认无误就好。
其次,pyodbc传递给freetds会进行一次转码,怎么转,转成什么,不知道,也没有更多信息或者文档支持和论证这个问题。
最后,FreeTDS会将任何接收到的东西采用UCS2编码的方式转码后传递到数据库,写入。(http://www.freetds.org/userguide/unicodefreetds.htm)
所以,如果1、3我都测试通过没有问题,那么症结就在pyodbc转码传递给FreeTDS的问题了。
python不支持UCS2编码,所以没有办法采用这种方式转码,而FreeTDS也不能明文改成这种编码的字符串。
pyodbc的文档非常悲剧,啥都没有,那看看StackOverFlow是否有相关的文献提供支持?
无意中翻阅了文献2和文献3,文献2表示pyodbc的用法嘛,是不建议你通过拼接变量的方式进行赋值的,而是使用“?”表达式(暂且这么称呼吧)来赋值。
例如:
问题描述中的插入语句我原本是写成:
SqlStr = "insert into t(name) values(%s)" % (a)
按照pyodbc的问号表达式的写法,应该写成:
SqlStr = "insert into t(name) values(?)" % (a)
这不是传说中的换汤不换药么?
回答的人轻轻地表示,这样做之后,就能搞定了。
然后捏,然后文献2的作者然并卵地说他自己笨了,原来是系统的语言环境没有设置对。
我就不在意了。
文献3里面,有回答表示:“丫的,你写入nvarchar字段,加个N呀”。
按照他的想法,应该直接这样:
SqlStr = u"insert into t(name) values(N'测试')"
我就兴高采烈屁颠屁颠地试了一下,放屁。
然后二楼就接话了:“孩子,你还年轻,想当年我年轻的时候,用pyodbc的问号表达式的赋值方法,就解决你这个问题了。”
真的么?
果然……
解决方案:
1. 首先你的数据库至少是UTF-8编码的,正常来说,我们大天朝子民,一般都使用 Chinese_PRC_CI_AS
2. python脚本文件本身是UTF-8编码的(在linux下用file命令查看,得到结果:test.py UTF-8 Unicode Java program text)
3. python脚本第一行加上文件编码的定义,两种写法均可:(1)# -*-coding:utf-8-*-;(2)# coding:utf-8
4. python脚本中,中文(或者泛称unicode字符串)需要使用如下格式:u'中文'
5. FreeTDS配置需加上 client charset = UTF-8 (不知道加哪里看我的提问贴求助:Linux下使用Pyodbc插入数据到MSSQL中文乱码 )
6. 脚本中,涉及到pyodbc insert的语句,采用如下方式执行:
sql = "insert into tpayon_test (name) values (?)"
parameters = (u"中文")
cursor.execute(sql, parameters)
以上6点,缺一不可。
最后兴奋地贴一个图吧:
-------------------------------------------------------------
参考文献:
1. using pyodbc on linux to insert unicode or utf-8 chars in a nvarchar mssql field
3. why insert empty value using pyodbc in Linux environment?
4. pyodbc官方文档