You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用pyodbc向SQL Server插入数据时出现日期插入错误

问题描述

向SQL Server插入数据时持续收到如下报错:
pyodbc.DataError: ('22007', '[22007] [Microsoft][ODBC SQL Server Driver][SQL Server]Conversion failed when converting date and/or time from character string. (241) (SQLExecDirectW)')

已尝试所有可想到的日期格式化方案,仍无法正常执行插入语句,复现代码如下:

data = pd.read_csv("tasks to upload join.csv")
df = pd.DataFrame(data)
df['ScheduledDate'] = pd.to_datetime(df['ScheduledDate'], format='%d/%m/%Y %H:%M').dt.strftime('%d/%m/%Y %H:%M:%S')

cursor = conn.cursor()
for row in df.itertuples():
    cursor.execute('''
    INSERT INTO Tasks
           (TaskTypeId, ScheduledDate, IsEmailAlert, ScheduledContactId, DealId, AccountId, Name, EmployeeId,
           TaskStatusId, secondaryId, Created, CompletedDate, ReminderDate, isPopUpAlert)
    values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)
    ''',
            row.TaskTypeId, row.ScheduledDate, row.IsEmailAlert, row.defaultcontactid, row.dealid, row.accountid,
            row.Name, row.EmployeeId, row.TaskStatus, row.SecondaryIdTask, row.Created, completedDate, completedDate,
            isPopUpAlert
            )

conn.commit()
故障原因

报错本质是SQL Server收到的日期格式字符串不符合解析规则,或传入了非日期类型的脏数据,和代码逻辑相关的触发点有3个:

  • 将ScheduledDate转成了%d/%m/%Y %H:%M:%S格式的字符串传入,SQL Server默认日期解析规则不会优先匹配「日/月/年」格式,遇到日期值大于12的记录时会直接误判为月份,触发转换失败。
  • 传入的row.Created是pandas自带的Timestamp类型、或CSV读取到的原始字符串,没有做类型适配,部分非法值(空字符串、不存在的日期、非日期文本)会直接触发转换报错。
  • 代码中硬编码传入的completedDate变量未做类型校验,且ReminderDate字段位置重复传入了completedDate,如果该变量是字符串格式、或存在空值/非法值,也会触发同类报错。
修复方案

核心原则:给pyodbc传日期参数时,不要传自定义格式的字符串,直接传Python原生datetime对象,驱动会自动完成类型适配,完全规避字符串转日期的格式冲突问题。

  1. 调整DataFrame日期字段处理逻辑,不要转字符串,保留datetime类型,同时提前筛出脏数据:
# 解析日期列,解析失败直接抛错定位脏数据,不要转字符串
df['ScheduledDate'] = pd.to_datetime(df['ScheduledDate'], format='%d/%m/%Y %H:%M', errors='raise')
df['Created'] = pd.to_datetime(df['Created'], errors='raise')

# 提前排查空值/非法日期,提前处理不要传到SQL层
print("ScheduledDate空值行数:", df['ScheduledDate'].isna().sum())
print("Created空值行数:", df['Created'].isna().sum())
  1. 插入前把pandas的Timestamp类型转成Python原生datetime对象,允许为空的日期字段传None,不要传空字符串:
cursor = conn.cursor()
for row in df.itertuples():
    # 转原生datetime,空值传None
    sched_date = row.ScheduledDate.to_pydatetime() if pd.notna(row.ScheduledDate) else None
    create_time = row.Created.to_pydatetime() if pd.notna(row.Created) else None
    
    # 提前校验硬编码的completedDate、isPopUpAlert类型:
    # 1. completedDate如果是日期值,必须是原生datetime类型,空值传None
    # 2. 确认ReminderDate字段是否真的需要传completedDate,避免业务逻辑错误
    cursor.execute('''
    INSERT INTO Tasks
           (TaskTypeId, ScheduledDate, IsEmailAlert, ScheduledContactId, DealId, AccountId, Name, EmployeeId,
           TaskStatusId, secondaryId, Created, CompletedDate, ReminderDate, isPopUpAlert)
    values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)
    ''',
            row.TaskTypeId, sched_date, row.IsEmailAlert, row.defaultcontactid, row.dealid, row.accountid,
            row.Name, row.EmployeeId, row.TaskStatus, row.SecondaryIdTask, create_time, completedDate, completedDate,
            isPopUpAlert
            )
conn.commit()

内容的提问来源于stack exchange,提问作者Augustine Uba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 01:30:48