メインコンテンツへスキップ

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples on this page work on VillageSQL. Install Now →
MySQL には UPSERT キーワードはありませんが、行を挿入するか競合を処理する方法が 3 つあります。INSERT ... ON DUPLICATE KEY UPDATE、REPLACE INTO、および INSERT IGNORE です。これらの動作は大きく異なり、間違ったものを選ぶと、黙ってデータを破損させることがあります。

INSERT … ON DUPLICATE KEY UPDATE

挿入が一意制約(主キーまたは一意インデックス)に違反する場合、MySQL は代わりに UPDATE を実行します。
(既存の行ではなく)挿入しようとした値を参照するには、行エイリアスを使用します。
古い VALUES(col) 構文も動作しますが、非推奨です。代わりに行エイリアスを使用してください。

REPLACE INTO

REPLACE は競合する行を削除し、新しい行を挿入します。UPSERT のように見えますが、削除と挿入のように動作します。
これは対象テーブルの BEFORE DELETE トリガーと AFTER DELETE トリガーを発火させ、新しい行のために AUTO_INCREMENT カウンターを進めます。この削除は実際の行の削除であるため、参照しているテーブルの ON DELETE CASCADE やその他の外部キーアクションは発火します。REPLACE に列挙されていない列は、前の行の値ではなくデフォルト値になります。 REPLACE は、古い行を完全に破棄したい場合にのみ使用してください。

INSERT IGNORE

INSERT IGNORE はエラーを警告に格下げします。キーの競合ではその行をスキップし、型の不一致や切り捨てでは代わりに強制変換した値を書き込みます。
INSERT IGNORE の問題は、重複だけでなくすべてのエラーを抑制することであり、重複以外のケースはスキップされた行よりも深刻です。INSERT IGNORE INTO page_views (page, views) VALUES ('/new', 'not_a_number') は、警告だけを出して ('/new', 0) を挿入します。何もしないのではなく、黙って破損した書き込みを行うのです。結果が本当にどうでもよい場合にのみ使用してください。

比較

AUTO_INCREMENT の副作用

ON DUPLICATE KEY UPDATE は、挿入ではなく更新の経路を取った場合でも AUTO_INCREMENT カウンターを進めます。時間とともに、これが連番の欠番を生みます。page_views は page を主キーとして使用し、AUTO_INCREMENT 列を持たないため、この効果を確認するには代理キーを持つテーブルを使用します。
これは想定された動作です。アプリケーションが欠番のない連番に依存する場合は、別のカウンターテーブルを使用するか、アプリケーション側で連番の生成を処理してください。

よくある質問

ON DUPLICATE KEY UPDATE はアトミックですか?

はい。挿入の試行と更新は、1 つのアトミックな操作として行われます。存在確認と挿入の間の競合状態を避けるために、トランザクションで包む必要はありません。

複数の一意インデックスが競合した場合はどうなりますか?

挿入が複数の一意制約に同時に違反する場合、MySQL は 1 行だけを更新します。どの行かは未定義です。1 つの INSERT が複数の一意インデックスで競合しうる設計は避けてください。スキーマに重なり合う一意制約がある場合は、動作を慎重にテストしてください。

複数行の挿入で ON DUPLICATE KEY UPDATE を使用できますか?

はい。
競合する各行が、それぞれ自身の更新を引き起こします。行エイリアスを使う場合は、複数行のケースでも VALUES を使い、エイリアスには AS new を使います。

トラブルシューティング

関連項目