1. 程式人生 > >解決使用Pyodbc向MSSQL插入unicode字元亂碼的問題

解決使用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裡面的時候,會變成:

?μ?èˉ?


之類的東東,俗稱亂碼。


問題的詳細描述請看我在論壇的發帖:求助: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

2. pyODBC and Unicode

3. why insert empty value using pyodbc in Linux environment?

4. pyodbc官方文件