使用Cx_Oracle executemany批量插入字典列表触发TypeError问题
问题根源
当使用cx_Oracle的executemany批量插入字典列表时,驱动会根据列表中第一个字典的参数类型推断整个批次的参数类型。你的两个字典中,第一个的birthdate是datetime对象,因此驱动会要求后续所有行的birthdate也必须是datetime类型;但第二个字典的birthdate是空字符串'',无法匹配datetime类型,从而抛出类型错误。
单独处理每个字典时,驱动会针对单个行重新推断类型:处理第一个字典时按datetime处理,处理第二个字典时,空字符串可能被隐式转换为NULL,因此不会报错。
解决方案
方案1:统一将空字符串替换为None
cx_Oracle会将None映射为SQL的NULL,这是DATE类型字段空值的正确表示方式。在插入前预处理数据:
def prepare_debtor_rows(rows): for row in rows: # 处理birthdate字段,空字符串替换为None if row.get('birthdate') == '': row['birthdate'] = None # 同步处理其他可能的空字段 for key in ['lastname', 'firstname', 'patronymicname', 'birthplace']: if row.get(key) == '': row[key] = None return rows def send_data(connection , rows): try: target_rows = prepare_debtor_rows(rows[23:25]) with connection.cursor() as cursor: sql = """ INSERT INTO debtors( bankruptID,category,lastname,firstname,patronymicname, fullname,inn,snils,ogrn,region,address,birthdate, birthplace,guid,uploadBatchId ) VALUES ( :bankruptID,:category,:lastname,:firstname,:patronymicname, :fullname,:inn,:snils,:ogrn,:region,:address,:birthdate, :birthplace,:guid,:uploadBatchId ) """ cursor.executemany(sql, target_rows) connection.commit() except Exception as e: print(f"插入失败: {str(e)}") connection.rollback()
方案2:显式指定参数类型
通过cursor.setinputsizes()强制指定每个参数的数据库类型,避免驱动自动推断出错(建议配合方案1使用,确保空值格式正确):
import cx_Oracle def send_data(connection , rows): try: target_rows = prepare_debtor_rows(rows[23:25]) with connection.cursor() as cursor: # 显式指定每个参数的类型 cursor.setinputsizes( bankruptID=int, category=str, lastname=str, firstname=str, patronymicname=str, fullname=str, inn=str, snils=str, ogrn=str, region=str, address=str, birthdate=cx_Oracle.DATE, birthplace=str, guid=str, uploadBatchId=str ) sql = """ INSERT INTO debtors( bankruptID,category,lastname,firstname,patronymicname, fullname,inn,snils,ogrn,region,address,birthdate, birthplace,guid,uploadBatchId ) VALUES ( :bankruptID,:category,:lastname,:firstname,:patronymicname, :fullname,:inn,:snils,:ogrn,:region,:address,:birthdate, :birthplace,:guid,:uploadBatchId ) """ cursor.executemany(sql, target_rows) connection.commit() except Exception as e: print(f"插入失败: {str(e)}") connection.rollback()
关键注意点
executemany的类型推断是批次级别的,而非逐行推断,这是批量插入性能优化的一部分,但会导致混合类型的行报错。- 数据库DATE类型字段的空值必须用
NULL(对应Python的None),空字符串不是有效的DATE值,即使单独插入时未报错,也属于驱动的隐式兼容,不推荐依赖。
内容的提问来源于stack exchange,提问作者johnthesilly
相关产品推荐
相关产品推荐

