22

我正在使用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

我是这样配置/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

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

[global]
    tds version = 8.0
    client charset = UTF-8

31e2fae4adbf1b2af1726e5668a3414cf46b454f我已从中获取pyodbc 修订版并使用“ ”http://github.com/mkleehammer/pyodbc安装它python setup.py install

我的本地网络上有一台装有Microsoft SQL Server 2000的 Windows 机器,启动并监听本地 IP 地址 10.32.42.69。我有一个名为“Common”的空数据库。我有用户“sa”,密码为“secret”,拥有完全权限。

我正在使用以下 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()

到目前为止一切正常。我在服务器上使用了 SQLServer 的企业管理器,新表就在那里。现在我想在表上插入一些数据。

cur = con.cursor()
cur.execute('INSERT INTO testing (name) VALUES (?)', (u'something',))

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

pyodbc.Error: ('HY004', '[HY004] [FreeTDS][SQL Server]Invalid data type 
(0) (SQLBindParameter)'

由于我的客户端配置为使用 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]

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

<type 'unicode'> somé string

也就是说,pyodbc 不会直接接受 unicode 对象,但它会将 unicode 对象返回给我!并且编码被混淆了!

现在的问题:

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

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

这要求不高,是吗?

4

4 回答 4

22

我记得使用 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')

就试一试吧。很有趣。解码后的字符串应该是“Hey my name is André”,但实际上是“Hey my name is Andrテゥ”。é 被日语 テゥ 取代

因此我的建议是:您需要确保 pyodbc 能够直接以 Unicode 格式发送数据。如果 pyodbc 没有做到这一点,你会得到意想不到的结果。

我以客户端到服务器的方式描述了这个问题。但是当从服务器返回到客户端时,也会出现同样的问题。如果客户端无法理解 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 通信,所以你应该没问题。

免费TDS 0.82

但是,cramm0表示 FreeTDS 0.82 并非完全没有错误,并且 0.82 与官方修补的 0.82 版本之间存在显着差异,可在此处找到。您可能应该尝试使用已修补的 FreeTDS


编辑删除旧数据,与 FreeTDS 无关,仅与 Easysoft 商业 odbc 驱动有关。对不起。

于 2009-06-08T13:06:21.500 回答
2

我使用 UCS-2 与 SQL Server 交互,而不是 UTF-8。

更正:我更改了 .freetds.conf 条目,以便客户端使用 UTF-8

    tds version = 8.0
    client charset = UTF-8
    text size = 32768

现在,绑定值适用于 UTF-8 编码的字符串。驱动程序在用于数据服务器端存储的 UCS-2 和提供给客户端/从客户端获取的 UTF-8 编码字符串之间进行透明转换。

这是运行 Python 2.5 和 FreeTDS freetds-0.82.1.dev.20081111 和 SQL Server 2008 的 Solaris 10 上的 pyodbc 2.0

导入pyodbc
test_string = u"""评论 ça va ? Très bien ?"""

打印类型(test_string),repr(test_string)
utf8 = 'utf8:' + test_string.encode('UTF-8')
打印类型(utf8),repr(utf8)

c = pyodbc.connect('DSN=SA_SQL_SERVER_TEST;UID=XXX;PWD=XXX')

cur = c.cursor()
# 这不起作用,因为 test_string 不是 UTF 编码的
尝试:
    cur.execute('INSERT unicode_test(t) VALUES(?)', test_string)
    c.commit()
除了 pyodbc.Error,e:
    打印 e


# 这个是:
尝试:
    cur.execute('INSERT unicode_test(t) VALUES(?)', utf8)
    c.commit()
除了 pyodbc.Error,e:
    打印 e    


这是测试表的输出(我通过 Management Studio 手动输入了一堆测试数据)

在 [41] 中:对于 i in cur.execute('SELECT t FROM unicode_test'):
   .....:打印我
   .....:
   .....:
(“这不是香蕉”,)
('\xc3\x85kergatan 24', )
('\xc3\x85kergatan 24', )
('\xe6\xb0\xb4 这是代码点 63CF', )
('Mich\xc3\xa9l', )
('评论 va ? Trs bien ?', )
('utf8:Comment \xc3\xa7a va ? Tr\xc3\xa8s bien ?', )

我能够通过“编辑前 200 行”对话框将一些 unicode 代码点直接从 Management Studio 放入表中,并输入 unicode 代码点的十六进制数字,然后按 Alt-X

于 2009-06-05T01:28:17.420 回答
1

我在尝试绑定 unicode 参数时遇到了同样的问题:'[HY004] [FreeTDS][SQL Server]Invalid data type (0) (SQLBindParameter)'

我通过将 freetds 升级到 0.91 版解决了这个问题。

我使用 pyodbc 2.1.11。我必须应用补丁才能使其与 unicode 一起使用,否则我偶尔会遇到内存损坏错误。

于 2011-10-28T12:59:48.193 回答
0

您确定是 INSERT 导致无法阅读吗?pyodbc Problem fetching NTEXT and NVARCHAR data上存在一个错误。

于 2009-06-15T03:50:11.117 回答