【问题标题】:Inserting unicode escape sequence data to SQL server db (pyodbc)将 unicode 转义序列数据插入 SQL server db (pyodbc)
【发布时间】:2016-08-23 17:36:41
【问题描述】:

我正在尝试将从网络上抓取的数据插入到 SQL server db 中,并且我的 db 表的相关列设置为nvarchar(max) 类型。

在 python shell 中测试:

dbargs={'DATABASE': '<mydbname>',
 'DRIVER': '{FreeTDS}',
 'PORT': '1433',
 'PWD': '<mypass>',
 'SERVER': '<server>',
 'UID': '<myusername>'}

import pyodbc
cnxn = pyodbc.connect(**dbargs)
cursor = cnxn.cursor()

insert_cmd="INSERT INTO c_master (run_id, product_name) VALUES (?,?)"

然后

cursor.execute(insert_cmd, (274, u'test naméâôóòöë'))

工作正常,但是

cursor.execute(insert_cmd, (274, u'test \u2019d'))

导致错误

ProgrammingError: ('42000', "[42000] [FreeTDS][SQL Server]Incorrect syntax near ','. (102) (SQLExecDirectW)")

我发现当我尝试将其插入到写入器管道中的数据库中时,我正在抓取后一种类型的数据并产生错误。

处理此类数据的正确方法是什么?

(我使用的是 FreeTDS、unixodbc、MSSQL Server、pyodbc)

FreeTDS 和 unixodbc 配置:

/etc/odbc.init:

[myserver]
Driver = FreeTDS
Description =Myserver MSSQL database
# Servername corresponds to the section in freetds.conf
Servername=myserver
Database = mydbname
TDS_Version = 7.0

/etc/odbcinst.ini:

[ODBC]
Trace = Yes
TraceFile = /tmp/odbcsql.log
ForceTrace = Yes
Pooling = Yes

[FreeTDS]
Description = TDS driver (Sybase/MS SQL)
Driver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so
Setup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so
CPTimeout = 120 
CPReuse =

/etc/freetds/freetds.conf:

[global]
tds version = 7.0


dump file = /var/log/freetds.log
debug flags = 0xffff

# timeout = 10
# connect timeout = 10

text size = 64512


[myserver]
host = <myserverip>
port = 1433
tds version = 7.0
dump_file = /var/log/freetds.log
client charset = UTF-8

我还将 TDSDUMP="/var/log/freetds.log" 添加到 /etc/environment 和 /etc/profile。虽然很奇怪,但我仍然没有看到 freetds 日志。

【问题讨论】:

  • 你好,有几个问题。您使用的是 Python 2 还是 Python 3?要正确使用 Unicode,您还必须将 TDS_Version 传递给连接字符串 - 您是否在 DSN 定义中这样做?
  • Python 2.7。我设置了 TDS_Versionin /etc/odbc.ini。我现在将更新我的问题以包含这些配置文件。
  • 查看 /tmp/freetds.log 似乎显示版本 4.2 尽管我的配置。我想这可能是问题所在!任何想法为什么我的 conf 没有给我 7.0 版?
  • @FlipperPA 我还发现,如果我按照您所说的将“TDS_Version”传递给连接字符串,它就可以工作!很明显,我的 freetds conf 只是被忽略或损坏了?
  • 我已经提交了一些关于 TDS 版本的更多信息作为答案。

标签: sql-server unicode pyodbc freetds


【解决方案1】:

您需要将TDS_Version 作为连接字符串的一部分传递。您正在使用完整的连接字符串进行连接,如果您在 connect() 声明中将完整的服务器名称作为 SERVER 传递,它将绕过 DSN。如果要连接 DSN,则需要提供 DSN(和 UID / PWD)而不是 SERVERPORT

我已发出拉取请求,以便在此处更好地向 FreeTDS 解释 TDS 版本:https://github.com/FreeTDS/freetds/pull/71

如果未提供,则使用的默认 TDS 版本不支持 Unicode(如您所见!)。假设您使用的是 SQL Server 2005 或更高版本,如果您正在使用:

  • FreeTDS 1.0:使用 TDS_Version 7.4
  • FreeTDS 0.95:使用 TDS_Version 7.3
  • FreeTDS 0.91:使用 TDS_Version 7.2

祝你好运!

【讨论】:

  • 我理解你所说的绕过DSN,但为什么它不尊重全局freedns.conf“tds版本”?
  • 这是个好问题;老实说,我不知道,但我怀疑它只会在您使用 DSN 时才会命中文件。
【解决方案2】:

我怀疑您需要配置 FreeTDS 连接以识别将用于插入语句的客户端编码。

在 ODBC 中,我不知道如何为单个参数声明编码。所有 SQL 文本和字符数据都根据单个客户端编码连接配置进行解释。

您还可以设置TDSDUMP 环境参数,并查看发送到您的服务器的数据。如果您还没有做到这一点,请尝试 ODBC 日志。

【讨论】:

  • 好吧,我的 freetds conf 设置为 7.0 版,客户端字符集 = UTF-8。就像我说的,大多数 unicode 是默认处理的,它似乎是 unicode 转义序列,如 u'\u2019'
  • 那么我认为这是 Python 的事情:“unicode 文字”没有定义编码为 UTF-8 的字符串。如果您将文字显式转换为该编码的 Unicode 类型,一切都会好起来的。
【解决方案3】:

我遇到了同样的问题,一切正常,唯一的问题是有“?”插入语句后的表中的字符。我正在使用 Python 3.7.2。

所以当我使用类似于以下查询时,它终于起作用了。

insert_cmd="""INSERT INTO c_master (run_id, product_name) VALUES ('{runId}',N'{productName}')""".format(runId=4, productName='حلواہ پوری')

【讨论】:

  • 如果你不小心format的参数,这可能会导致SQL注入
猜你喜欢
  • 2019-12-27
  • 2023-04-04
  • 1970-01-01
  • 2012-03-24
  • 1970-01-01
  • 1970-01-01
  • 2021-03-29
  • 2020-05-12
  • 1970-01-01
相关资源
最近更新 更多