Python程序SQL Insert命令在不同pyodbc版本下执行失败求助
问题描述
我编写了一个Python程序,通过Insert语句向SQL Server导入数据。在本地PC使用pyodbc 4.0.0版本时运行正常,但在UNIX服务器上使用pyodbc 4.0.26版本执行Insert时失败,抛出以下错误:
pyodbc.Error: ('07006', '[07006][Openlink][ODBC][Driver]Restricted datatype attribute violation (0)(SQLBindparameter)')
执行Insert前的DELETE语句可正常清空表数据,相关代码如下:
cursor.execute("DELETE FROM [dbo].[Python_Table]") f.write("deleted rows from Python Table") f.write("\n====================\n") db.commit() for index, row in df.iterrows(): cursor.execute("INSERT INTO dbo.Python_Table ([ID], [Compliant_Ind], [Mappings], [Govt_Req_1_Ind], [Govt_Req_2_Ind], [GLBA_Req_3_Ind],[GLBA_Req_4_Ind],[GLBA_Req_5_Ind], [Enterprise_Issue], [Classification_Date], [Comments], [Load_Date]) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?)", row['ID'], row['Compliant_Ind'], row['Mappings'], row['Govt_Req_1_Ind'], row['Govt_Req_2_Ind'],row['Govt_Req_3_Ind'],row['Govt_Req_4_Ind'],row['Govt_Req_5_Ind'], row['Enterprise_Issue'], row['Classification_Date'], row['Comments'],row['load_date']) db.commit()
格式化后的Insert语句:
"INSERT INTO dbo.Python_Table ( [ID], [Compliant_Ind], [Mappings], [Govt_Req_1_Ind], [Govt_Req_2_Ind], [GLBA_Req_3_Ind], [GLBA_Req_4_Ind], [GLBA_Req_5_Ind], [Enterprise_Issue], [Classification_Date], [Comments], [Load_Date]) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", row['ID'], row['Compliant_Ind'], row['Mappings'], row['Govt_Req_1_Ind'], row['Govt_Req_2_Ind'], row['Govt_Req_3_Ind'], row['Govt_Req_4_Ind'], row['Govt_Req_5_Ind'], row['Enterprise_Issue'], row['Classification_Date'], row['Comments'], row['load_date']
解决思路
1. 核对数据类型兼容性
这个错误核心原因是Python数据类型与SQL Server列类型不匹配,pyodbc新版本对类型绑定的校验更严格:
- 逐个匹配DataFrame字段与SQL表
Python_Table的列类型:- 日期类型列(如
Classification_Date、Load_Date):确保DataFrame中是Python原生datetime对象,而非numpy.datetime64或字符串,可通过pd.to_datetime()转换后再插入。 - 布尔标识列(如
Compliant_Ind系列字段):若SQL Server列是bit类型,需保证DataFrame中的值是Pythonbool类型,而非int或字符串。 - 字符串列:检查是否存在内容长度超过SQL列定义长度的情况,新版本对长度限制的校验更严格。
- 日期类型列(如
2. 显式指定参数绑定类型
pyodbc新版本可能调整了默认参数绑定逻辑,显式指定类型可避免自动转换错误:
import pyodbc # 整理参数列表 params = ( row['ID'], row['Compliant_Ind'], row['Mappings'], row['Govt_Req_1_Ind'], row['Govt_Req_2_Ind'], row['Govt_Req_3_Ind'], row['Govt_Req_4_Ind'], row['Govt_Req_5_Ind'], row['Enterprise_Issue'], row['Classification_Date'], row['Comments'], row['load_date'] ) # 对应SQL Server列的参数类型(需根据实际表结构调整) param_types = [ pyodbc.SQL_INTEGER, pyodbc.SQL_BIT, pyodbc.SQL_VARCHAR, pyodbc.SQL_BIT, pyodbc.SQL_BIT, pyodbc.SQL_BIT, pyodbc.SQL_BIT, pyodbc.SQL_BIT, pyodbc.SQL_VARCHAR, pyodbc.SQL_DATETIME, pyodbc.SQL_VARCHAR, pyodbc.SQL_DATETIME ] # 绑定参数类型后执行插入 cursor.setinputsizes(*param_types) cursor.execute(insert_sql, params)
3. 排查ODBC驱动差异
UNIX服务器使用的Openlink ODBC驱动,与本地Windows常用的微软官方ODBC驱动行为可能存在差异:
- 更新Openlink ODBC驱动至最新版本,修复可能存在的类型绑定兼容性问题。
- 替换为微软官方的MS ODBC Driver for SQL Server(Linux版),该驱动与pyodbc新版本的兼容性更稳定。
4. 改用批量插入替代逐行插入
逐行插入不仅效率低,还容易触发类型绑定问题,改用executemany()或pandas.to_sql()优化:
用executemany批量插入
# 整理所有数据为列表 data_list = [] for index, row in df.iterrows(): data_list.append(( row['ID'], row['Compliant_Ind'], row['Mappings'], row['Govt_Req_1_Ind'], row['Govt_Req_2_Ind'], row['Govt_Req_3_Ind'], row['Govt_Req_4_Ind'], row['Govt_Req_5_Ind'], row['Enterprise_Issue'], row['Classification_Date'], row['Comments'], row['load_date'] )) # 批量执行插入 insert_sql = "INSERT INTO dbo.Python_Table ([ID], [Compliant_Ind], [Mappings], [Govt_Req_1_Ind], [Govt_Req_2_Ind], [GLBA_Req_3_Ind],[GLBA_Req_4_Ind],[GLBA_Req_5_Ind], [Enterprise_Issue], [Classification_Date], [Comments], [Load_Date]) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?,?)" cursor.executemany(insert_sql, data_list) db.commit()
用pandas.to_sql直接插入
# 注意:需确保DataFrame列名与SQL表列名大小写一致(UNIX系统大小写敏感) df.rename(columns={'load_date': 'Load_Date'}, inplace=True) df.to_sql('Python_Table', con=db, schema='dbo', if_exists='append', index=False)
5. 检查字段名大小写一致性
UNIX系统大小写敏感,代码中row['load_date']与SQL表列[Load_Date]大小写不一致,可能导致驱动解析异常。建议统一列名大小写,比如将DataFrame列名改为Load_Date后再插入。
内容的提问来源于stack exchange,提问作者user19210181
相关产品推荐
相关产品推荐

