Python写入pandas数据到MS SQL Server出现TDS错误的原因排查
报错根本原因
报错指向的第9个参数,对应INSERT语句里的Entry_Date字段,核心问题是pandas的NaN/NaT和SQL Server的空值NULL不兼容:
- pandas里的空值
NaN底层是numpy.float64类型,即使是日期字段里的空值NaT,在逐行取数传给ODBC驱动时,也不会被自动识别为SQL的NULL,反而会被当成非法的浮点值传入datetime类型的字段,直接触发TDS流校验错误。 - 你提到
ENTRY_PLAZA、ENTRY_DATE字段存在部分NaN值,刚好和报错指向的参数位置完全吻合,和你核对的字段类型定义无关,是空值类型不匹配导致的。
修复方案
按以下步骤处理即可解决:
- 第一步:统一替换DataFrame中的空值,把所有pandas自带的
NaN/NaT替换为Python原生的None——只有原生None会被pyodbc/ODBC驱动正确识别为SQL Server的NULL值,替换代码:# 先把所有空值替换为原生None df = df.where(pd.notna(df), None) - 第二步:提前强制对齐字段类型,避免隐式转换出错:
# 日期字段统一转datetime类型,转换失败的值强制置空再替换为None df["POSTING_DATE"] = pd.to_datetime(df["POSTING_DATE"], errors="coerce") df["ENTRY_DATE"] = pd.to_datetime(df["ENTRY_DATE"], errors="coerce") df["EXIT_DATE"] = pd.to_datetime(df["EXIT_DATE"], errors="coerce") # 数值字段统一转数值类型,转换失败的值强制置空 df["MILES"] = pd.to_numeric(df["MILES"], errors="coerce") df["TOLL_AMOUNT"] = pd.to_numeric(df["TOLL_AMOUNT"], errors="coerce") # 类型转换完再做一次空值替换 df = df.where(pd.notna(df), None) - 第三步:修正插入逻辑,逐行插入时直接传入处理后的行元组即可,不需要手动逐个取row[1]到row[15],避免位置数错:
# itertuples加index=False,就不会带最前面的索引位 for row in df.itertuples(index=False): MsExe.execute(""" INSERT INTO test_tolls_table_temp (PostingDate, Source, ReadType, PPTagID, ETagID_Plate, EquipID, Agency, Entry_Plaza, Entry_Date, Exit_Plaza, Exit_Date, Toll_Class, Miles, Toll_Amount, ID) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, row) # 记得执行完批量提交 MsExe.commit()
如果数据量较大,不建议用逐行循环插入的写法,可以直接用pandas自带的to_sql方法配合SQLAlchemy连接做批量插入,性能会高很多,也能自动处理大部分类型兼容问题。
内容的提问来源于stack exchange,提问作者Oscar Gutierrez
相关产品推荐
相关产品推荐

