在linux上使用pyodbc在nvarchar mssql字段中插入unicode或utf-8字符

nos*_*klo 21 python sql-server unicode utf-8 pyodbc

我正在使用Ubuntu 9.04

我安装了以下软件包版本:

unixodbc and unixodbc-dev: 2.2.11-16build3
tdsodbc: 0.82-4
libsybdb5: 0.82-4
freetds-common and freetds-dev: 0.82-4
Run Code Online (Sandbox Code Playgroud)

我这样配置/etc/unixodbc.ini:

[FreeTDS]
Description             = TDS driver (Sybase/MS SQL)
Driver          = /usr/lib/odbc/libtdsodbc.so
Setup           = /usr/lib/odbc/libtdsS.so
CPTimeout               = 
CPReuse         = 
UsageCount              = 2
Run Code Online (Sandbox Code Playgroud)

我这样配置/etc/freetds/freetds.conf:

[global]
    tds version = 8.0
    client charset = UTF-8
Run Code Online (Sandbox Code Playgroud)

我已经抓住pyodbc修订31e2fae4adbf1b2af1726e5668a3414cf46b454fhttp://github.com/mkleehammer/pyodbc,并使用安装了" python setup.py install"

我在我的本地网络上安装了一台安装了Microsoft SQL Server 2000的Windows机器,然后侦听本地IP地址10.32.42.69.我有一个名为"Common"的空数据库.我有用户"sa"密码"秘密"与完全权限.

我使用以下python代码来设置连接:

import pyodbc
odbcstring = "SERVER=10.32.42.69;UID=sa;PWD=secret;DATABASE=Common;DRIVER=FreeTDS"
con = pyodbc.connect(s)
cur = con.cursor()
cur.execute('''
CREATE TABLE testing (
    id INTEGER NOT NULL IDENTITY(1,1), 
    name NVARCHAR(200) NULL, 
    PRIMARY KEY (id)
)
    ''')
con.commit()
Run Code Online (Sandbox Code Playgroud)

一切WORKS了这一点.我在服务器上使用过SQLServer的企业管理器,新表就在那里.现在我想在表格中插入一些数据.

cur = con.cursor()
cur.execute('INSERT INTO testing (name) VALUES (?)', (u'something',))
Run Code Online (Sandbox Code Playgroud)

那失败了!! 这是我得到的错误:

pyodbc.Error: ('HY004', '[HY004] [FreeTDS][SQL Server]Invalid data type 
(0) (SQLBindParameter)'
Run Code Online (Sandbox Code Playgroud)

由于我的客户端配置为使用UTF-8,我想我可以通过将数据编码为UTF-8来解决.这有效,但后来我得到了奇怪的数据:

cur = con.cursor()
cur.execute('DELETE FROM testing')
cur.execute('INSERT INTO testing (name) VALUES (?)', (u'somé string'.encode('utf-8'),))
con.commit()
# fetching data back
cur = con.cursor()
cur.execute('SELECT name FROM testing')
data = cur.fetchone()
print type(data[0]), data[0]
Run Code Online (Sandbox Code Playgroud)

这没有错误,但返回的数据与发送的数据不同!我明白了:

<type 'unicode'> somé string
Run Code Online (Sandbox Code Playgroud)

也就是说,pyodbc不会直接接受unicode对象,但会将unicode对象返回给我!编码正在混淆!

现在提问:

我希望代码在NVARCHAR和/或NTEXT字段中插入unicode数据.当我回询时,我想要插回相同的数据.

这可以通过不同的方式配置系统,或者使用能够在插入或检索时将数据正确地转换为unicode /从unicode转换数据的包装函数

这不是很多,是吗?

Nic*_*zet 21

我记得使用odbc驱动程序有这种愚蠢的问题,即使那时它是一个java + oracle组合.

核心问题是odbc驱动程序在将查询字符串发送到数据库时显然会对其进行编码.即使该字段是Unicode,并且如果您提供Unicode,在某些情况下它似乎并不重要.

您需要确保驱动程序发送的内容与数据库具有相同的编码(不仅是服务器,还包括数据库).否则,当然你会得到时髦的角色,因为客户端或服务器在编码/解码时会混合起来.您是否知道您的服务器用作解码数据的默认值的字符集(代码点如MS所说)?

整理与此问题无关:)

例如,请参阅MS页面.对于Unicode字段,排序规则仅用于定义列中的排序顺序,而不用于指定数据的存储方式.

如果您将数据存储为Unicode,则有一种独特的方式来表示它,这就是Unicode的目的:无需定义与您将要使用的所有语言兼容的字符集:)

这里的问题是"当我向服务器提供 Unicode的数据时会发生什么?".例如:

  • 当我向服务器发送UTF-8字符串时,它是如何理解的?
  • 当我向服务器发送UTF-16字符串时,它是如何理解的?
  • 当我将Latin1字符串发送到服务器时,它如何理解它?

从服务器的角度来看,所有这3个字符串只是一个字节流.服务器无法猜测您对其进行编码的编码.这意味着如果你的odbc客户端最终向服务器发送字节串(一个编码的字符串)而不是发送unicode数据,你将会遇到麻烦:如果这样做,服务器将使用预定义的编码(这是我的问题:什么编码服务器将使用?因为它不是猜测,它必须是一个参数值),如果字符串已经使用不同的编码进行编码,dzing,数据将被破坏.

它与在Python中完全相似:

uni = u'Hey my name is André'
in_utf8 = uni.encode('utf-8')
# send the utf-8 data to server
# send(in_utf8)

# on server side
# server receives it. But server is Japanese.
# So the server treats the data with the National charset, shift-jis:
some_string = in_utf8 # some_string = receive()    
decoded = some_string.decode('sjis')
Run Code Online (Sandbox Code Playgroud)

就试一试吧.好有趣.解码后的字符串应该是"嘿,我的名字是André",但是"嘿,我的名字是Andrテゥ".é被日本テgets取代

因此我的建议是:你需要确保pyodbc能够直接将数据作为Unicode发送.如果pyodbc无法执行此操作,您将获得意外结果.

我在Client to Server方式中描述了这个问题.但是,当从服务器回传到客户端时,会出现同样的问题.如果客户端无法理解Unicode数据,您可能会遇到麻烦.

FreeTDS为您处理Unicode.

实际上,FreeTDS为您处理事情并将所有数据转换为UCS2 unicode.(来源).

  • 服务器< - > FreeTDS:UCS2数据
  • FreeTDS < - > pyodbc:编码的字符串,以UTF-8编码(来自/etc/freetds/freetds.conf)

因此,如果您将UTF-8数据传递给pyodbc,我希望您的应用程序能够正常工作.事实上,正如这张django-pyodbc票据所述,django-pyodbc以UTF-8与pyodbc通信,所以你应该没问题.

FreeTDS 0.82

但是,cramm0表示FreeTDS 0.82并非完全没有bug,并且0.82和官方修补的0.82版本之间存在显着差异,可以在这里找到.您可能应该尝试使用修补的FreeTDS


编辑:删除旧数据,这与FreeTDS无关,但仅与Easysoft商业odbc驱动程序有关.抱歉.