Python Pandas中将NaN转为Oracle空值遇DPI-1055错误求助
解决Oracle插入DataFrame时NaN导致的DPI-1055错误
首先咱们先拆解你遇到的核心问题:
- 你用
df.where(pd.notnull(df), None)没生效,是因为pandas的where方法默认返回新的DataFrame,不会修改原数据——你没把结果赋值回df,所以原表的NaN根本没被替换。 - 就算你做了赋值,pandas的数值类型列(比如
float64)无法直接存储None,会自动把None转回NaN,导致替换操作等于没做,插入时还是触发错误。
接下来给你几个可行的解决方案,按推荐程度排序:
方案1:正确替换NaN为None并处理数值列类型
先把所有NaN替换为None,同时把数值列转为object类型(这样才能保留None,避免被自动转回NaN)。修改后的代码如下:
import pandas as pd def insertData(table_name, schema_name, df): if not df.empty: # 关键修改:先转object类型,再替换NaN为None df = df.astype(object).where(pd.notnull(df), None) values = df.to_dict(orient='records') table = sa.Table(table_name , conndict['meta'], autoload=True, schema=schema_name) result_proxy = conndict['conn'].execute(table.insert(), values) return result_proxy else: return None
如果担心全表转object影响性能,可以只针对数值列单独处理:
def insertData(table_name, schema_name, df): if not df.empty: # 处理数值列:将NaN替换为None num_cols = df.select_dtypes(include=['int64', 'float64']).columns for col in num_cols: df[col] = df[col].apply(lambda x: None if pd.isna(x) else x) # 处理非数值列:直接替换NaN为None non_num_cols = df.select_dtypes(exclude=['int64', 'float64']).columns df[non_num_cols] = df[non_num_cols].where(pd.notnull(df[non_num_cols]), None) values = df.to_dict(orient='records') table = sa.Table(table_name , conndict['meta'], autoload=True, schema=schema_name) result_proxy = conndict['conn'].execute(table.insert(), values) return result_proxy else: return None
这样处理后,DataFrame里的NaN会被替换成Python的None,SQLAlchemy插入时会自动把None映射为Oracle的NULL,完美解决DPI-1055错误。
方案2:利用SQLAlchemy类型绑定自动转换NaN
如果你不想修改DataFrame的类型,可以在插入语句中指定数值列的类型,让SQLAlchemy自动把NaN转为NULL:
from sqlalchemy import Numeric, bindparam def insertData(table_name, schema_name, df): if not df.empty: values = df.to_dict(orient='records') table = sa.Table(table_name , conndict['meta'], autoload=True, schema=schema_name) # 为数值列创建带类型绑定的参数,自动处理NaN为NULL insert_values = [] for row in values: bind_params = {} for col, val in row.items(): if table.c[col].type.python_type in (int, float): bind_params[col] = bindparam(col, type_=Numeric, nullable=True) else: bind_params[col] = val insert_values.append(bind_params) result_proxy = conndict['conn'].execute(table.insert(), insert_values) return result_proxy else: return None
这个方法的核心是用bindparam指定数值类型,SQLAlchemy会自动识别NaN并转为Oracle的NULL。
方案3:使用pandas内置的to_sql方法(最简洁)
如果你的场景允许,直接用pandas的to_sql会更省心,它已经内置了NaN转NULL的处理逻辑:
def insertData(table_name, schema_name, df): if not df.empty: df.to_sql( name=table_name, schema=schema_name, con=conndict['conn'], if_exists='append', # 可根据需求选择append/replace/fail index=False ) return True else: return None
to_sql会自动处理数值列的NaN,将其转为Oracle的NULL,无需手动替换,代码也更简洁。
内容的提问来源于stack exchange,提问作者Varun Khanna
相关产品推荐
相关产品推荐

