pandas.to_sql使用method='multi'写入Oracle报CompileError无orig属性错误
问题根因
- Oracle 原生 SQL 语法不支持 Pandas
to_sql方法中method='multi'默认生成的多行插入格式:标准SQL的INSERT INTO 表 (列1,列2) VALUES (val1,val2), (val3,val4)写法,Oracle 不支持单条INSERT语句后跟多个VALUES组,因此SQLAlchemy在编译该不兼容SQL时会抛出CompileError,你看到的属性报错是异常对象处理过程中的衍生问题。 - Netezza 支持上述标准多行插入语法,因此运行无异常。
解决方案
方案1:自定义Oracle批量插入方法(性能最优)
Pandas的to_sql的method参数支持传入自定义可调用对象,我们可以基于cx_Oracle原生的executemany能力实现批量插入,性能远高于默认单条插入,和multi方法性能相当。
先定义适配函数:
def oracle_bulk_insert(table, conn, keys, data_iter): # 拼接插入SQL语句 col_names = ', '.join(keys) # Oracle占位符使用:1、:2格式 placeholders = ', '.join([f':{idx+1}' for idx in range(len(keys))]) insert_sql = f"INSERT INTO {table.schema}.{table.name} ({col_names}) VALUES ({placeholders})" # 转换待插入数据格式 insert_data = list(data_iter) # 调用cx_Oracle原生批量执行方法 conn.connection.executemany(insert_sql, insert_data) # 提交事务 conn.connection.commit()
然后修改queryResultToTable方法中的to_sql调用逻辑,根据目标库类型选择对应method:
# 先判断目标库是否为Oracle if 'oracle' in targetDBEngineURL: insert_method = oracle_bulk_insert else: insert_method = 'multi' queryResult.to_sql( targetTableName, targetDBConnection, schema=targetSchemaName, if_exists='append', index=False, dtype=targetDataTypes, method=insert_method, chunksize=5000 # 可根据数据量调整每次批量插入的行数,避免内存占用过高 )
方案2:分片单条插入(改动最小)
如果不想修改太多代码,可保留method=None,增加chunksize参数设置分片插入,比如设置chunksize=1000,每次插入1000条数据,性能相比全量单条插入也会有明显提升,完全可以满足大多数场景需求。
方案3:适配Oracle 12c+多行语法(不推荐)
如果你使用的是Oracle 12c及以上版本,也可以自定义生成符合Oracle规范的INSERT ALL多行插入语句,但该写法性能不如原生executemany,仅作为备选方案。
注意事项
- 使用自定义批量插入方法时,不需要额外设置
autocommit=True,可在自定义函数内统一提交事务即可。 - 插入大数据量时,建议合理设置
chunksize参数,避免单次插入数据量过大导致内存溢出或数据库事务过大。
内容的提问来源于stack exchange,提问作者George
相关产品推荐
相关产品推荐

