【问题标题】:How to upsert pandas DataFrame to Microsoft SQL Server table?如何将 pandas DataFrame 插入 Microsoft SQL Server 表?
【发布时间】:2020-10-04 21:32:00
【问题描述】:

我想将我的 pandas DataFrame 插入到 SQL Server 表中。 This question 为 PostgreSQL 提供了一个可行的解决方案,但 T-SQL 没有 INSERTON CONFLICT 变体。我怎样才能为 SQL Server 完成同样的事情?

【问题讨论】:

    标签: python sql-server pandas sqlalchemy upsert


    【解决方案1】:

    有两种选择:

    1. 使用MERGE 语句代替INSERT ... ON CONFLICT
    2. 使用UPDATE 语句和JOIN,后跟有条件的INSERT

    T-SQL documentation for MERGE 说:

    性能提示:当两个表具有复杂的匹配特征混合时,为 MERGE 语句描述的条件行为最有效。例如,如果行不存在则插入,如果匹配则更新行。当简单地根据另一个表的行更新一个表时,使用基本的 INSERT、UPDATE 和 DELETE 语句来提高性能和可伸缩性。

    在许多情况下,简单地使用单独的 UPDATEINSERT 语句会更快、更简单。

    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')]
    

    【讨论】:

    • 有关可与复合(多列)主键一起使用的示例,请参阅this answer
    • 我正在尝试在当前用例中复制第 1 步:我正在创建 sqlalchemy 引擎,如下所示: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)')你知道为什么吗?
    • @TheDude - 试试df1.to_sql("TEMP", engine, schema="WWNEXPORT", index=False, if_exists="replace")
    • 不幸的是,这不是解决方案,我得到了同样的错误。这是错误附带的一些附加信息,由于 cmets 的字符限制,我无法在上面的评论中发布:[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 语句是如何以及为什么生成的。
    • @TheDude - pandas to_sql() 正在调用 SQLAlchemy has_table() 以查看表是否已存在,因此 SQLAlchemy 正在查询 SYSCAT(元数据)表以查看您的表是否显示在那里。不幸的是,我没有使用 ibm_db_sa 的经验。
    猜你喜欢
    • 1970-01-01
    • 2021-01-19
    • 2018-08-13
    • 2020-08-05
    • 2020-01-28
    • 2017-06-04
    • 2018-01-21
    • 2019-09-26
    • 1970-01-01
    相关资源
    最近更新 更多