Linux에서 pyodbc를 사용하여 nvarchar mssql 필드에 유니코드 또는 utf-8 문자 삽입

StackOverflow https://stackoverflow.com/questions/947077

문제

나는 사용하고있다 우분투 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

나는 pyodbc 개정판을 잡았습니다 31e2fae4adbf1b2af1726e5668a3414cf46b454f ~에서 http://github.com/mkleehammer/pyodbc "를 사용하여 설치했습니다.python setup.py install"

나는 Windows 컴퓨터를 가지고 있습니다. 마이크로소프트 SQL 서버 2000 내 로컬 네트워크에 설치되어 로컬 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는 유니코드 객체를 직접 허용하지 않지만 유니코드 객체를 나에게 다시 반환합니다!그리고 인코딩이 혼동되고 있습니다!

이제 질문은 다음과 같습니다.

NVARCHAR 및/또는 NTEXT 필드에 유니코드 데이터를 삽입하는 코드를 원합니다.다시 쿼리할 때 삽입한 것과 동일한 데이터를 다시 원합니다.

이는 시스템을 다르게 구성하거나 삽입 또는 검색 시 데이터를 유니코드와 올바르게 변환할 수 있는 래퍼 기능을 사용함으로써 가능합니다.

그다지 많은 것을 요구하는 것은 아니죠?

도움이 되었습니까?

해결책

odbc 드라이버를 사용할 때 이런 종류의 어리석은 문제가 있었던 것을 기억합니다. 당시에는 java+oracle 조합이었음에도 불구하고 말입니다.

핵심은 odbc 드라이버가 쿼리 문자열을 DB로 보낼 ​​때 분명히 쿼리 문자열을 인코딩한다는 것입니다.필드가 유니코드인 경우에도 유니코드를 제공하는 경우에는 문제가 되지 않는 경우가 있습니다.

드라이버가 보낸 내용이 데이터베이스(서버뿐만 아니라 데이터베이스)와 동일한 인코딩을 가지고 있는지 확인해야 합니다.그렇지 않으면 물론 클라이언트나 서버가 인코딩/디코딩할 때 내용을 뒤섞기 때문에 이상한 문자가 표시됩니다.서버가 데이터 디코딩을 위한 기본값으로 사용하는 문자 세트(MS가 말하는 코드 포인트)에 대해 알고 있습니까?

데이터 정렬은 이 문제와 관련이 없습니다 :)

보다 그 MS 페이지 예를 들어.유니코드 필드의 경우 데이터 정렬은 열의 정렬 순서를 정의하는 데에만 사용됩니다. ~ 아니다 데이터 저장 방법을 지정합니다.

데이터를 유니코드로 저장하는 경우 이를 표현하는 고유한 방법이 있습니다. 이것이 바로 유니코드의 목적입니다.사용하려는 모든 언어와 호환되는 문자 세트를 정의할 필요가 없습니다 :)

여기서 질문은 "서버에 데이터를 제공하면 어떻게 됩니까?"입니다. ~ 아니다 유니코드?".예를 들어:

  • UTF-8 문자열을 서버에 보낼 때 서버는 이를 어떻게 이해합니까?
  • UTF-16 문자열을 서버에 보낼 때 서버는 이를 어떻게 이해합니까?
  • Latin1 문자열을 서버에 보내면 서버는 이를 어떻게 이해합니까?

서버 관점에서 보면 이 세 문자열은 모두 바이트 스트림일 뿐입니다.서버는 사용자가 인코딩한 인코딩을 추측할 수 없습니다.즉 당신은 ~ 할 것이다 odbc 클라이언트가 전송을 끝내면 문제가 발생합니다 바이트열 (인코딩된 문자열)을 보내는 대신 서버로 유니코드 데이터:그렇게 하면 서버는 미리 정의된 인코딩을 사용하게 됩니다. (내 질문은 다음과 같습니다.서버는 어떤 인코딩을 사용할 것인가?추측이 아니므로 매개변수 값이어야 함), 문자열이 다른 인코딩을 사용하여 인코딩된 경우 , 데이터가 손상됩니다.

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 내 이름은 André입니다"로 되어 있지만 "Hey 내 이름은 Andrテゥ"입니다.é는 일본어 テゥ로 대체됩니다.

따라서 내 제안은 다음과 같습니다.pyodbc가 데이터를 유니코드로 직접 보낼 수 있는지 확인해야 합니다.pyodbc가 이 작업을 수행하지 못하면 예상치 못한 결과가 발생합니다.

그리고 클라이언트 대 서버 방식으로 문제를 설명했습니다.그러나 서버에서 클라이언트로 다시 통신할 때도 동일한 종류의 문제가 발생할 수 있습니다.클라이언트가 유니코드 데이터를 이해할 수 없으면 문제가 발생할 가능성이 높습니다.

FreeTDS가 유니코드를 처리합니다.

실제로 FreeTDS가 모든 작업을 처리하고 모든 데이터를 UCS2 유니코드로 변환합니다.(원천).

  • 서버 <--> FreeTDS:UCS2 데이터
  • FreeTDS <--> pyodbc :UTF-8로 인코딩된 인코딩된 문자열( /etc/freetds/freetds.conf)

따라서 UTF-8 데이터를 pyodbc에 전달하면 응용 프로그램이 올바르게 작동할 것으로 기대합니다.사실 이와 같이 django-pyodbc 티켓 상태, django-pyodbc는 pyodbc와 UTF-8로 통신하므로 괜찮을 것입니다.

무료TDS 0.82

하지만, 벼락치기0 FreeTDS 0.82는 완전히 버그가 없으며 0.82와 공식 패치 0.82 버전 사이에는 상당한 차이가 있다고 말합니다. 여기.아마도 패치된 FreeTDS를 사용해 보아야 할 것입니다.


편집됨: FreeTDS와는 관련이 없지만 Easysoft 상업용 odbc 드라이버에만 관련된 오래된 데이터를 제거했습니다.죄송합니다.

다른 팁

저는 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을 사용하는 경우입니다.

import pyodbc
test_string = u"""Comment ça va ? Très bien ?"""

print type(test_string),repr(test_string)
utf8 = 'utf8:' + test_string.encode('UTF-8')
print type(utf8), repr(utf8)

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

cur = c.cursor()
# This does not work as test_string is not UTF-encoded
try: 
    cur.execute('INSERT unicode_test(t) VALUES(?)', test_string)
    c.commit()
except pyodbc.Error,e:
    print e


# This one does:
try:
    cur.execute('INSERT unicode_test(t) VALUES(?)', utf8)
    c.commit()
except pyodbc.Error,e:
    print e    


다음은 테스트 테이블의 출력입니다(Management Studio를 통해 수동으로 많은 테스트 데이터를 입력했습니다).

In [41]: for i in cur.execute('SELECT t FROM unicode_test'):
   ....:     print i
   ....:
   ....:
('this is not a banana', )
('\xc3\x85kergatan 24', )
('\xc3\x85kergatan 24', )
('\xe6\xb0\xb4 this is code-point 63CF', )
('Mich\xc3\xa9l', )
('Comment a va ? Trs bien ?', )
('utf8:Comment \xc3\xa7a va ? Tr\xc3\xa8s bien ?', )

'상위 200개 행 편집' 대화 상자에서 유니코드 코드 포인트의 일부를 Management Studio의 테이블에 직접 입력하고 유니코드 코드 포인트에 대한 16진수를 입력한 다음 Alt-X를 누를 수 있었습니다.

유니코드 매개변수를 바인딩하려고 할 때도 동일한 문제가 발생했습니다.'[HY004] [FreeTDS][SQL Server]잘못된 데이터 유형(0)(SQLBindParameter)'

freetds를 0.91 버전으로 업그레이드하여 문제를 해결했습니다.

나는 pyodbc 2.1.11을 사용합니다.신청해야 했어요 이것 유니코드에서 작동하도록 패치를 적용했습니다. 그렇지 않으면 가끔 메모리 손상 오류가 발생했습니다.

읽지 못하는 문제를 일으키는 것이 INSERT인가요?pyodbc에 버그가 열려 있습니다. NTEXT 및 NVARCHAR 데이터를 가져오는 중 문제 발생.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top