PAYATHON 2026

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 中的时间格式或空值规则不同,请相应调整读取和转换逻辑。

备注:内容仅供参考。