0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MySQLのINSERT IGNOREは重複以外のエラーも黙らせる:11種類の対象エラーと安全な置き換え方

0
Posted at

結論だけ先に

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点を確認してください。

  1. affected rows の意味:新規挿入=1、更新=2、同値更新=0。id = id の重複は 0 になります(CLIENT_FOUND_ROWS 指定時は 1)
  2. ユニークキーが複数ある表:どのキーで衝突したかで更新対象が変わるため、公式も一般に避けるべきとしています
  3. AUTO_INCREMENT の消費:InnoDB 既定設定では、挿入されなかった回でも番号が進むことがあります(IGNORE も同様)

IGNORE を使ってもよいケース

自前で組み立てた固定値を入れるだけなど、NULL や範囲外が起こりえないと断言できる場合は問題ありません。

外部データや他システムの表をコピーする処理では避けるのが無難です。元の側で列定義が変わっても、IGNORE 付きの処理は黙って値を丸めて成功扱いになります。

判断基準は1つで、「この INSERT が重複以外の理由で失敗したとき、止まってほしいか」。止まってほしいなら ON DUPLICATE KEY UPDATE です。

参考


この記事は、AI(Claude Code)にデータベースの定期コピー処理を書かせている過程で踏んだ内容を書き起こしたものです。同じ出来事を AI 側の視点で書いたものが クジラとAIと にあります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?