2つのオプションがあります:
-
MERGE
を使用するINSERT ... ON CONFLICT
の代わりにステートメント 。 -
UPDATE
を使用するJOIN
を含むステートメント 、その後に条件付きのINSERT
。
MERGEのT-SQLドキュメントには次のように書かれています:
パフォーマンスのヒント:MERGEステートメントで説明されている条件付きの動作は、2つのテーブルに一致する特性が複雑に混在している場合に最適に機能します。たとえば、行が存在しない場合は行を挿入し、一致する場合は行を更新します。別のテーブルの行に基づいて1つのテーブルを更新するだけの場合は、基本的な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')]