解決使用Pyodbc向MSSQL插入unicode字元亂碼的問題
——這個問題煩擾了我將近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中文亂碼 [問題點數:100分]
CSDN確實不夠給力。
只有一條回帖,而且沒有啥價值(給點提示和意見都好啊~哭)。
簡單的來說,就是,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中文亂碼 [問題點數:100分])
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官方文件