できない.dev

MySQL で「Duplicate entry」が出て INSERT できない

Duplicate entry は PRIMARY KEY か UNIQUE 制約に同じ値を入れようとしたときのエラーである。
エラー文の末尾に出るキー名から、どの制約に当たったかを特定するのが先決。
重複を許すのか、更新に倒すのか、無視するのかで対処が変わる。

公開:

要約

ERROR 1062 (23000): Duplicate entry 'alice@example.com' for key 'users.email_unique' は、一意性制約に同じ値を入れようとしたときのエラーである。
読むべきは for key の後ろに出るキー名で、これがどの制約に当たったかを直接示している。

mysql> INSERT INTO users (email) VALUES ('alice@example.com');
ERROR 1062 (23000): Duplicate entry 'alice@example.com' for key 'users.email_unique'

キー名が PRIMARY なら主キー、それ以外なら同名のユニークインデックスである。
まずここを確定させてから、重複をどう扱うかを決める。

よくある原因

  1. 単純な重複: すでにある値を入れようとしている。
    アプリ側で存在チェックをしてから INSERT していても、チェックと INSERT の間に別のリクエストが入れば起きる。
  2. AUTO_INCREMENT のずれ: id を明示して INSERT したり、ダンプを流し込んだりすると、次の採番値がテーブルの最大値より小さいままになる。
    以降の INSERT が既存 id とぶつかる。
  3. 複合ユニークキーの見落とし: UNIQUE (tenant_id, email) のような制約では、email だけを見ても重複判定はできない。
    組み合わせ全体で一意である必要がある。
  4. 照合順序による同一視: utf8mb4_0900_ai_ci のような大文字小文字を区別しない照合順序では、Alice@example.comalice@example.com は同じ値として扱われる。
  5. 二重実行: ジョブのリトライやリクエストの二重送信で、同じ行を 2 回入れている。

解決策

1. どのキーに当たったかを特定する

SHOW CREATE TABLE users\G

出力の UNIQUE KEY / PRIMARY KEY の定義と、エラー文のキー名を突き合わせる。
複合キーなら構成列をすべて確認する。

2. 既存行の更新に倒す

INSERT INTO users (email, name) VALUES ('alice@example.com', 'Alice')
  ON DUPLICATE KEY UPDATE name = VALUES(name);

公式ドキュメント(新しいタブで開く) のとおり、PRIMARY KEYUNIQUE インデックスの重複が起きたときに UPDATE へ切り替わる。
影響行数は新規挿入なら 1、更新なら 2 が返る。

3. 重複を捨ててよい場合

INSERT IGNORE INTO users (email) VALUES ('alice@example.com');

IGNORE は重複エラーを警告に落として処理を続ける。
ただし重複以外のエラーも警告に変わるため、取り込みバッチのような用途に限定する。

4. AUTO_INCREMENT を実データに合わせる

SELECT MAX(id) FROM users;
ALTER TABLE users AUTO_INCREMENT = 1001;

ダンプを取り込んだ直後に一度実行しておくと、以降の採番衝突を防げる。

5. 競合そのものを前提にする

存在チェックしてから INSERT する方式は競合状態を防げない。
一意性制約を残したまま、Duplicate entry を捕まえて「既に登録済み」として扱う実装にする方が安全である。

この記事は役立ちましたか?