【发布时间】:2020-10-04 21:32:00
【问题描述】:
我想将我的 pandas DataFrame 插入到 SQL Server 表中。 This question 为 PostgreSQL 提供了一个可行的解决方案,但 T-SQL 没有 INSERT 的 ON CONFLICT 变体。我怎样才能为 SQL Server 完成同样的事情?
【问题讨论】:
标签: python sql-server pandas sqlalchemy upsert
我想将我的 pandas DataFrame 插入到 SQL Server 表中。 This question 为 PostgreSQL 提供了一个可行的解决方案,但 T-SQL 没有 INSERT 的 ON CONFLICT 变体。我怎样才能为 SQL Server 完成同样的事情?
【问题讨论】:
标签: python sql-server pandas sqlalchemy upsert
有两种选择:
MERGE 语句代替INSERT ... ON CONFLICT。UPDATE 语句和JOIN,后跟有条件的INSERT。T-SQL documentation for MERGE 说:
性能提示:当两个表具有复杂的匹配特征混合时,为 MERGE 语句描述的条件行为最有效。例如,如果行不存在则插入,如果匹配则更新行。当简单地根据另一个表的行更新一个表时,使用基本的 INSERT、UPDATE 和 DELETE 语句来提高性能和可伸缩性。
在许多情况下,简单地使用单独的 UPDATE 和 INSERT 语句会更快、更简单。
engine = sa.create_engine(
connection_uri, fast_executemany=True, isolation_level="SERIALIZABLE"
)
with engine.begin() as conn:
# step 0.0 - create test environment
conn.execute(sa.text("DROP TABLE IF EXISTS main_table"))
conn.execute(
sa.text(
"CREATE TABLE main_table (id int primary key, txt varchar(50))"
)
)
conn.execute(
sa.text(
"INSERT INTO main_table (id, txt) VALUES (1, 'row 1 old text')"
)
)
# step 0.1 - create DataFrame to UPSERT
df = pd.DataFrame(
[(2, "new row 2 text"), (1, "row 1 new text")], columns=["id", "txt"]
)
# step 1 - upload DataFrame to temporary table
df.to_sql("#temp_table", conn, index=False, if_exists="replace")
# step 2 - merge temp_table into main_table
conn.execute(
sa.text("""\
UPDATE main SET main.txt = temp.txt
FROM main_table main INNER JOIN #temp_table temp
ON main.id = temp.id
"""
)
)
conn.execute(
sa.text("""\
INSERT INTO main_table (id, txt)
SELECT id, txt FROM #temp_table
WHERE id NOT IN (SELECT id FROM main_table)
"""
)
)
# step 3 - confirm results
result = conn.execute(sa.text("SELECT * FROM main_table ORDER BY id")).fetchall()
print(result) # [(1, 'row 1 new text'), (2, 'new row 2 text')]
【讨论】:
sa.create_engine("ibm_db_sa+pyodbc://?driver=IBM i Access ODBC Driver&SYSTEM=XXX&;Port=21&UID=XXX&PWD=XXX&Database=") 然后执行第 1 步:df1.to_sql("WWNEXPORT.TEMP", engine, index=False, if_exists="replace") 但我收到以下错误: sqlalchemy.exc.ProgrammingError: (pyodbc.ProgrammingError) ('42S02', '[42S02] [IBM][System i Access ODBC Driver][DB2 for i5/OS]SQL0204 - TABLES of type *FILE in SYSCAT not found. (-204) (SQLPrepare)')你知道为什么吗?
df1.to_sql("TEMP", engine, schema="WWNEXPORT", index=False, if_exists="replace")
[SQL: SELECT "SYSCAT"."TABLES"."TABNAME" FROM "SYSCAT"."TABLES" WHERE "SYSCAT"."TABLES"."TABSCHEMA" = ? AND "SYSCAT"."TABLES"."TABNAME" = ?] [parameters: ('WWNEXPORT', 'TEMP')] (Background on this error at: https://sqlalche.me/e/14/f405) 我不明白这个 SQL 语句是如何以及为什么生成的。
to_sql() 正在调用 SQLAlchemy has_table() 以查看表是否已存在,因此 SQLAlchemy 正在查询 SYSCAT(元数据)表以查看您的表是否显示在那里。不幸的是,我没有使用 ibm_db_sa 的经验。