Skip to content

行をアップサートする(INSERT か UPDATE を1文で) snippet

アップサートは、行が存在しなければ挿入し、存在すれば更新します。1つの文で行うため、読んでから書く競合(race)はありません。素朴な2ステップ(SELECT してから INSERT または UPDATE)は、並行処理下でデータを壊します。2つの書き手がどちらも SELECT をミスして、どちらも INSERT してしまうのです。ON CONFLICT が Postgres の答えで、MySQL は ON DUPLICATE KEY UPDATE と綴り、SQLite は Postgres の構文を踏襲します。正しさを決めるのは2点です。競合ターゲットは本物のユニーク制約か主キーでなければならず、DO UPDATE では、挿入に失敗した値を読むために excluded.* 疑似テーブルが必要です。

アップサートは、行が存在しなければ挿入し、存在すれば更新します。1つの文で行うため、読んでから書く競合(race)はありません。素朴な2ステップ(SELECT してから INSERT または UPDATE)は、並行処理下でデータを壊します。2つの書き手がどちらも SELECT をミスして、どちらも INSERT してしまうのです。ON CONFLICT が Postgres の答えで、MySQL は ON DUPLICATE KEY UPDATE と綴り、SQLite は Postgres の構文を踏襲します。正しさを決めるのは2点です。競合ターゲットは本物のユニーク制約か主キーでなければならず、DO UPDATE では、挿入に失敗した値を読むために excluded.* 疑似テーブルが必要です。

Runnable recipe · 1 languages
Files & Datasqlupsertinserton-conflictpostgres

Every language

1 languages, copy-ready. One at a time with syntax highlighting, or all inline.

SQLSQLrunnable
INSERT INTO users (id, email, name)
VALUES (42, 'ada@example.com', 'Ada')
ON CONFLICT (id) DO UPDATE
    SET email = excluded.email,
        name  = excluded.name
RETURNING id, (xmax = 0) AS inserted_this_time;

excluded holds the row that failed to insert — read the challenger's values from it, not from the table. ON CONFLICT DO NOTHING is the ignore-duplicates variant. Run it twice in the playground: first pass inserts (true), second pass updates (false).

Run in the SQL playground →

Keep going

Read the database-objects cheatsheet →