結論だけ先に
INSERT IGNORE は「重複した行だけを飛ばす」書き方ではありません。
- 重複キー以外に、NOT NULL 違反・外部キー違反・パーティション不一致など計11種類のエラーを警告に格下げします
- 範囲外の数値や長すぎる文字列は、エラーにならず近い値に丸めて挿入されます
- strict mode より IGNORE が優先されます。サーバー設定を厳しくしても防げません
重複だけを飛ばしたいなら、こう書きます。
INSERT INTO t (...) VALUES (...)
ON DUPLICATE KEY UPDATE id = id;
どんな場面で踏んだか
AI(Claude Code)に、あるDBのログを1時間ごとに別DBへ追記コピーする処理を書かせていました。元のログは古い行から上書きされて消えるので、その前に写しを残すためです。
取りこぼし防止で前回より少し手前から読み直すため、重複は毎回発生します。写し先にユニークキーを張り、重複分は捨てる、という設計で出てきたのがこれでした。
INSERT IGNORE INTO log_copy (source_id, event_type, logged_at, status)
VALUES (?, ?, ?, ?);
反映前に Codex でレビューしたところ、「IGNORE は重複以外のエラーも警告にする。重複だけ無視するなら ON DUPLICATE KEY UPDATE id = id に」と指摘され、公式ドキュメントを確認しました。
IGNORE の対象エラー一覧
MySQL リファレンス「The Effect of IGNORE on Statement Execution」より(8.0 / 5.7 共通)。
| エラー | 意味 | 重複? |
|---|---|---|
ER_DUP_ENTRY |
ユニークキー重複 | ○ |
ER_DUP_ENTRY_WITH_KEY_NAME |
同上(キー名付き) | ○ |
ER_DUP_KEY |
同上 | ○ |
ER_BAD_NULL_ERROR |
NOT NULL 列に NULL | × |
ER_NO_REFERENCED_ROW_2 |
外部キーの参照先が無い | × |
ER_ROW_IS_REFERENCED_2 |
参照されている親行の変更 | × |
ER_NO_PARTITION_FOR_GIVEN_VALUE |
該当パーティションなし | × |
ER_NO_PARTITION_FOR_GIVEN_VALUE_SILENT |
同上 | × |
ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET |
指定パーティション不一致 | × |
ER_SUBQUERY_NO_1_ROW |
サブクエリが1行でない | × |
ER_VIEW_CHECK_FAILED |
ビューの CHECK OPTION 違反 | × |
行の扱いは2パターン
| ケース | IGNORE なし | IGNORE あり |
|---|---|---|
| 重複キー | エラー | 行を捨てる+警告 |
| 外部キー参照先なし | エラー | 行を捨てる+警告 |
| NOT NULL 列に NULL | エラー(strict) | 暗黙のデフォルト値で挿入+警告 |
| 範囲外・長すぎる値 | エラー(strict) | 丸めて挿入+警告 |
「捨てる」は写しから行が消える、「挿入」は違う値のまま残る、ということです。ドキュメントの例では NOT NULL の数値列に (1),(NULL),(3) を入れると 1, 0, 3 になります。後から見て、その 0 が本物か NULL の化けたものかは判別できません。
既存の INSERT IGNORE を点検する
INSERT の直後、同じ接続で SHOW WARNINGS を実行します(次の文を実行すると警告は消えます)。
mysql> SHOW WARNINGS;
+---------+------+-----------------------------------------+
| Level | Code | Message |
+---------+------+-----------------------------------------+
| Warning | 1062 | Duplicate entry '1' for key 't.PRIMARY' |
+---------+------+-----------------------------------------+
-
1062… 重複。想定どおり -
1048(NULL)や値の範囲・長さ系のコード … 行が捨てられたか、値が丸められています
定期バッチなら「1062 以外の警告が1件でもあれば通知」にしておくと、平常時は静かで異常時だけ気づけます。PDO の ERRMODE_EXCEPTION を設定していても警告は例外にならないので、この確認は別途必要です。
ON DUPLICATE KEY UPDATE に置き換えるときの注意
INSERT INTO log_copy (source_id, event_type, logged_at, status)
VALUES (?, ?, ?, ?)
ON DUPLICATE KEY UPDATE id = id;
重複時は何も変えず、それ以外のエラーは通常どおり発生します。ただし次の3点を確認してください。
-
affected rows の意味:新規挿入=1、更新=2、同値更新=0。
id = idの重複は 0 になります(CLIENT_FOUND_ROWS指定時は 1) - ユニークキーが複数ある表:どのキーで衝突したかで更新対象が変わるため、公式も一般に避けるべきとしています
- AUTO_INCREMENT の消費:InnoDB 既定設定では、挿入されなかった回でも番号が進むことがあります(IGNORE も同様)
IGNORE を使ってもよいケース
自前で組み立てた固定値を入れるだけなど、NULL や範囲外が起こりえないと断言できる場合は問題ありません。
外部データや他システムの表をコピーする処理では避けるのが無難です。元の側で列定義が変わっても、IGNORE 付きの処理は黙って値を丸めて成功扱いになります。
判断基準は1つで、「この INSERT が重複以外の理由で失敗したとき、止まってほしいか」。止まってほしいなら ON DUPLICATE KEY UPDATE です。
参考
- MySQL 8.0 Reference Manual - The Effect of IGNORE on Statement Execution
- MySQL 8.0 Reference Manual - INSERT Statement
- MySQL 8.0 Reference Manual - INSERT ... ON DUPLICATE KEY UPDATE Statement
この記事は、AI(Claude Code)にデータベースの定期コピー処理を書かせている過程で踏んだ内容を書き起こしたものです。同じ出来事を AI 側の視点で書いたものが クジラとAIと にあります。