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」という名前で作成された空のデータベースがあります。完全な権限を持つパスワード「secret」を持つユーザー「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()

この時点まですべてが機能します。サーバーで SQLServer の Enterprise Manager を使用しましたが、新しいテーブルがそこにあります。ここで、テーブルにデータを挿入したいと思います。

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

当時はJavaとOracleの組み合わせだったとしても、odbcドライバーを使用してこの種のばかげた問題が発生したことを覚えています。

重要なことは、odbc ドライバーがクエリ文字列を DB に送信するときに明らかにエンコードすることです。フィールドが 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

UTF-8 ではなく、UCS-2 を使用して SQL Server と対話します。

修正: クライアントが UTF-8 を使用するように .freetds.conf エントリを変更しました

    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"""Comment ça va ? Très bien ?"""

印刷タイプ(テスト文字列),repr(テスト文字列)
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]: for i in cur.execute('SELECT t FROM unicode_test'):
   ....: 私を印刷
   ....:
   ....:
(「これはバナナではありません」、)
('\xc3\x85kergatan 24', )
('\xc3\x85kergatan 24', )
('\xe6\xb0\xb4 これはコードポイント 63CF', )
('Mich\xc3\xa9l', )
('Comment a va ? Trs bien ?', )
('utf8:コメント\xc3\xa7a va ? Tr\xc3\xa8s bien ?', )

[上位 200 行の編集] ダイアログで Unicode コード ポイントの 16 進数を入力し、Alt-X を押すことで、Unicode コード ポイントを Management Studio からテーブルに直接入れることができました。

于 2009-06-05T01:28:17.420 に答える
1

Unicode パラメーターをバインドしようとしたときに同じ問題が発生しました: '[HY004] [FreeTDS][SQL Server]無効なデータ型 (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 に答える