Используя pyodbc на linux для вставки символов unicode или utf-8 в поле nvarchar mssql
Я использую 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
Я взял ревизию podbc 31e2fae4adbf1b2af1726e5668a3414cf46b454f
из http://github.com/mkleehammer/pyodbc
и установил ее, используя "python setup.py install
"
У меня есть Windows-машина с Microsoft SQL Server 2000, установленная в моей локальной сети, вверх и прослушивание локального 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()
Все РАБОТЫ до этой точки. Я использовал 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 обратно ко мне! И кодировка смешивается!
Теперь на вопрос:
Я хочу, чтобы код вставлял данные Unicode в поле NVARCHAR и/или NTEXT. Когда я возвращаю запрос, я хочу, чтобы те же данные, которые я вставлял обратно.
Это может быть путем настройки системы по-разному или с помощью функции-обертки, способной правильно преобразовывать данные в/из юникода при вставке или извлечении
Что не спрашивает много, не так ли?
Ответы
Ответ 1
Я помню, что имел такие глупые проблемы с использованием драйверов odbc, даже если это время было комбинацией java + oracle.
Главное, что драйвер odbc, по-видимому, кодирует строку запроса при отправке в базу данных. Даже если поле является Unicode, и если вы предоставляете Unicode, в некоторых случаях это, похоже, не имеет значения.
Вам нужно убедиться, что то, что отправлено драйвером, имеет ту же кодировку, что и ваша база данных (не только сервер, но и база данных). В противном случае, конечно, вы получите напуганные символы, потому что либо клиент, либо сервер смешивают вещи при кодировании/декодировании. Есть ли у вас представление о кодировке (например, как MS), которую ваш сервер использует по умолчанию для декодирования данных?
Учет не имеет ничего общего с этой проблемой:)
См. эту страницу MS. Для полей Юникода сортировка используется только для определения порядка сортировки в столбце не, чтобы указать, как хранятся данные.
Если вы сохраняете свои данные как Unicode, есть уникальный способ его представления, что цель Unicode: нет необходимости определять кодировку, совместимую со всеми языками, которые вы собираетесь использовать:)
Вопрос здесь: "Что происходит, когда я даю данные серверу, который не является Unicode?". Например:
- Когда я отправляю на сервер строку UTF-8, как она ее понимает?
- Когда я отправляю на сервер строку UTF-16, как она ее понимает?
- Когда я отправляю на сервер строку Latin1, как она ее понимает?
С точки зрения сервера все эти 3 строки являются только потоком байтов. Сервер не может угадать кодировку, в которой вы их закодировали. Это означает, что вы получите проблемы, если ваш клиент odbc завершит отправку bytestrings (закодированной строки) на сервер вместо отправки данных 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')
Просто попробуй. Это весело. Декодированная строка должна быть "Эй, меня зовут Андре", но "Привет, меня зовут Андр". é заменяется японским テ ゥ
Следовательно, мое предложение: вам нужно обеспечить, чтобы pyodbc мог напрямую отправлять данные в виде Unicode. Если pyodbc не сможет этого сделать, вы получите неожиданные результаты.
И я описал проблему на пути "Клиент к серверу". Но такие же проблемы могут возникать при обращении от сервера к клиенту. Если Клиент не может понять данные Unicode, вы, вероятно, столкнетесь с проблемами.
FreeTDS обрабатывает Unicode для вас.
Собственно, FreeTDS заботится о вас и переводит все данные в Unicode UCS2. (Источник).
- Сервер ↔ Данные FreeTDS: UCS2
- FreeTDS ↔ pyodbc: закодированные строки, закодированные в UTF-8 (от
/etc/freetds/freetds.conf
)
Поэтому я ожидаю, что ваше приложение будет работать правильно, если вы передадите данные UTF-8 в pyodbc. Фактически, поскольку этот django-pyodbc ticket, django-pyodbc общается в UTF-8 с pyodbc, так что вы должны быть в порядке.
FreeTDS 0.82
Однако cramm0 говорит, что FreeTDS 0.82 не является полностью безнадежным и что между 0.82 и официальной исправленной версией 0.82 есть существенные различия, которые могут здесь. Вероятно, вы должны попробовать использовать исправленный FreeTDS
Отредактировано: удалены старые данные, которые не имели никакого отношения к FreeTDS, но были релевантны только для коммерческого драйвера odbc от Easysoft. К сожалению.
Ответ 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, используемым для хранения на стороне dataserver, и кодированными строками UTF-8, переданными/взятыми от клиента.
Это с pyodbc 2.0 на Solaris 10, работающим на Python 2.5 и FreeTDS freetds-0.82.1.dev.20081111 и SQL Server 2008
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 ?', )
Мне удалось добавить некоторые из кодов Unicode прямо в таблицу из Management Studio в диалоговом окне "Edit Top 200 rows" и ввести шестнадцатеричные цифры для кодовой точки Unicode, а затем нажать Alt-X
Ответ 3
У меня была такая же проблема при попытке привязать параметр unicode:
'[HY004] [FreeTDS] [SQL Server] Недопустимый тип данных (0) (SQLBindParameter)'
Я решил это, обновив freetds до версии 0.91.
Я использую pyodbc 2.1.11. Я должен был применить патч this, чтобы он работал с юникодом, в противном случае я иногда получал ошибки повреждения памяти.
Ответ 4
Вы уверены, что это INSERT, что вызывает проблему не чтения?
Там ошибка на pyodbc Проблема с загрузкой данных NTEXT и NVARCHAR.