VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
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 を使います。
トラブルシューティング
関連項目
- MySQL のトランザクション — UPSERT をトランザクションで包むべき場合
- MySQL のデッドロック — INSERT … ON DUPLICATE KEY UPDATE は並行実行時にデッドロックを引き起こすことがあります
- MySQL の一括挿入 — 競合処理の有無にかかわらず、多数の行を高速にロードする
- よくある MySQL エラーとその修正方法 — 重複キーエラーとその意味
- MySQL の INSERT ON DUPLICATE KEY UPDATE — このステートメント単体の、実例付きの解説