pandas to_sql 如何为 Oracle 列名加引号?
数据小陈
结论
quoted_name(table_name, True) 和 quoted_name(user_name, True) 会为表名和 schema 添加引用。to_sql 生成的列名是否带双引号,取决于列定义;dtype 只用于指定列类型,不控制列名引用。
Oracle 会将未加引号的标识符按大写处理。因此,DDL 中创建的 "last_updated_date" 与 INSERT 中未加引号的 last_updated_date 不匹配,可能触发 ORA-00904。
可以改用 SQLAlchemy Core 显式定义列,通过 quoted_name(..., True) 强制引用,再执行批量插入:
import pandas as pd
from sqlalchemy import Column, MetaData, String, Table, TIMESTAMP, create_engine
from sqlalchemy.sql import quoted_name
engine = create_engine(database_url)
metadata = MetaData()
table = Table(
quoted_name("my_table", True),
metadata,
Column(quoted_name("id_field", True), String(50)),
Column(quoted_name("last_updated_date", True), TIMESTAMP()),
schema=quoted_name("my_schema", True),
)
df = pd.read_csv(csv_file_path)
df["last_updated_date"] = pd.to_datetime(df["last_updated_date"])
records = df.to_dict(orient="records")
with engine.begin() as conn:
conn.execute(table.insert(), records)
SQLAlchemy 会根据这些列定义编译 INSERT,并按原样为列名添加双引号。请确认列名和类型与 Oracle 表结构一致。此处的类型对应示例 DDL 中的 VARCHAR2(50 CHAR) 和 TIMESTAMP (6)。
为什么原来的写法不够
问题中的 INSERT 是:
INSERT INTO "my_schema"."my_table" (id_field, last_updated_date)
VALUES (:id_field, :last_updated_date)
schema 和表名带了引号,两个列名却没有。Oracle 会将未加引号的 last_updated_date 解析为大写标识符 LAST_UPDATED_DATE,而 DDL 创建的是区分大小写的小写列名 "last_updated_date"。因此报错中会出现 "LAST_UPDATED_DATE"。
注意事项
- 如果继续使用
to_sql,需要确保它构建的 SQLAlchemy 列定义包含引用信息。仅给表名、schema 或dtype传入quoted_name,无法让列名带上引用。 - 上面的 Core 写法直接向已存在的表插入记录,不会通过
to_sql写入,因此不需要if_exists="append"。请勿对该表调用metadata.create_all()来替代现有建表流程。 - 示例用
pd.to_datetime将时间列转换为日期时间值。如果 CSV 中的时间格式或空值规则不同,请相应调整读取和转换逻辑。
备注:内容仅供参考。
