Linux上的pyodbc fast_executemany在插入时出现乱码

Posted

技术标签:

【中文标题】Linux上的pyodbc fast_executemany在插入时出现乱码【英文标题】:pyodbc fast_executemany on Linux garbles strings on insert 【发布时间】:2020-11-16 04:36:52 【问题描述】:

我有以下代码,在 Windows 上运行良好,在 Linux 上失败:

import pyodbc

conn = pyodbc.connect(p_str = None, server = ..., app = ..., databsae = ...,
                      driver = 'ODBC Driver 17 for SQL Server',
                      Trusted_Connection = 'yes')

conn.setdecoding(pyodbc.SQL_CHAR, encoding = 'utf-8')
conn.setdecoding(pyodbc.SQL_WCHAR, encoding = 'utf-8')
conn.setencoding(encoding = 'utf-8')

sql = 'INSERT INTO TestStrTbl(idKey,idValue) VALUES (?,?)'
data = [('one', 'value1'), ('two', 'value2')]

cursor = conn.cursor()
cursor.connection.autocommit = False
cursor.fast_executemany = True
cursor.executemany(sql, data)
cursor.commit()

我正在插入并清空使用以下 SQL 创建的 SQL Server 表:

CREATE TABLE TestStrTbl
(
    idKey   varchar(20) NOT NULL PRIMARY KEY,
    idValue varchar(20) NOT NULL
)

返回的错误是:

违反主键约束“PK__TestStrT__3FBEE7404FA9AB3B”。 无法在对象“dbo.TestStrTbl”中插入重复键。复制品 键值为 (?)。

当我使用相同的连接读取,或使用完整的字符串插入时,比如

sql = "INSERT INTO TestStrTbl(idKey,idValue) VALUES ('%s','%s')"
cursor.connection.autocommit = False
cursor.fast_executemany = True
for row in data:
    cursor.execute(sql % row)
cursor.commit()

这很好用。另一种可能性是让cursor.fast_executemany = False,那么它也可以在Linux 上运行。我能做些什么来解决这个问题?

我在 Ubuntu Linux 上使用pyodbc==4.0.24 运行 Python 3.7.8。 感谢您的帮助。


更新

这里是 unixODBC 设置:

18:48:56 $> odbcinst -j
unixODBC 2.3.1
DRIVERS............: /usr/local/etc/odbcinst.ini
SYSTEM DATA SOURCES: /usr/local/etc/odbc.ini
FILE DATA SOURCES..: /usr/local/etc/ODBCDataSources
USER DATA SOURCES..: /home/myUserId/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8

【问题讨论】:

根据错误消息,? 被作为文字字符串传递,而不是被参数值替换。这是 ODBC 驱动程序正上方的那一层,因为 T-SQL 不接受不带引号的?,所以你必须真正努力才能弄错。要查看此理论是否正确,请仅使用单行数据尝试此操作([('one', 'value1')] - 你应该在你的行中以文字结束?。或者,如果问题是编码问题,你仍然应该在行中获取一个值,告诉您出了什么问题。 此外,2019 年之前的 SQL Server 不支持 UTF-8,即便如此,也仅支持用于字段的特殊排序规则。 SQL Server 中的所有 Unicode 字符串都是 UTF-16。我对pyodbc 几乎一无所知,但如果那些setdecoding/setencoding 调用是必要的和/或正确的,我会感到惊讶。 Microsoft 提供的示例似乎不包括它们。 @JeroenMostert 一直困扰着我的是,除了具有快速执行的 Linux 之外,所有情况下都可以正常工作。 Windows(在任一模式下)或将 fast executemany 设置为 False 的 Linux 就像一个魅力。我也尝试使用latin 而不是utf-8,结果相同。 您绝对需要摆脱 conn.setdecodingconn.setencoding 语句。 SQL Server ODBC 不使用 UTF-8 编码。 (它使用 UTF-16LE,这是 pyodbc 的默认设置。)另外,从终端提示符运行 odbcinst -j 以检查您的 unixODBC 版本,如果它是旧的,请考虑升级它。 @GordThompson 谢谢。我用 unixODBC 设置更新了问题(在底部),我需要从那里升级吗? 【参考方案1】:

在使用 Microsoft 的 SQL Server ODBC 驱动程序时,无需调用 conn.setdecodingconn.setencoding。 pyodbc 的默认编码是 UTF16-LE,这是 Microsoft 的 ODBC 驱动程序使用的。

至于unixODBC,版本 2.3.5 (2018-01-02) 之前的任何内容都应该被视为“旧”,因为这是一个主要的错误修复版本。版本 2.3.7 (2018-08-10) 修复了几个错误,目前是推荐的版本。

【讨论】:

以上是关于Linux上的pyodbc fast_executemany在插入时出现乱码的主要内容,如果未能解决你的问题,请参考以下文章

M1 Mac 上的 Pyodbc

Windows 上的 pyodbc 和 python 3.4

SQL Server 上的 pyodbc - 如何进行插入并取回行 ID?

使用 pyodbc 从 Linux 向 Windows SQL Server 进行身份验证

linux使用pyodbc和freetds连接sqlserver

如何在 Linux 上使用 pyodbc 对 Active Directory 帐户使用用户/密码身份验证