MySQL 10 min read

MySQLのERROR 1062「Duplicate entry」 ― IGNORE・ON DUPLICATE KEY・REPLACEの違い

※この記事にはアフィリエイト広告(プロモーション)が含まれます。

主キーや一意キーと同じ値の行を入れようとすると、MySQL は次のエラーで止めます。

ERROR 1062 (23000): Duplicate entry 'a@example.com' for key 'users.email'

この記事のログは MySQL 8.4.11(Ubuntu 26.04 の標準パッケージ)で採りました。表は次のとおりです。

CREATE TABLE users (
  id    int AUTO_INCREMENT PRIMARY KEY,
  email varchar(100) NOT NULL UNIQUE,
  name  varchar(50)  NOT NULL,
  cnt   int NOT NULL DEFAULT 0
);

まず見る(結論)

  • for key '表名.キー名' でどのキーとぶつかったかが分かる。PRIMARY なら主キー
  • 重複したら更新したい → INSERT ... ON DUPLICATE KEY UPDATE
  • 重複したら何もしない → INSERT IGNORE。ただし重複以外のエラーも警告に変わる
  • REPLACE は既存の行を削除してから入れ直す。id が変わり、指定しなかった列は既定値に戻る

エラーの読み方

冒頭の例は一意キー email の重複です。主キーと同じ値を入れると PRIMARY と出ます。

INSERT INTO users (id, email, name) VALUES (1, 'c@example.com', 'C');
ERROR 1062 (23000): Duplicate entry '1' for key 'users.PRIMARY'

Duplicate entry の後がぶつかった値、for key の後が「表名.キー名」です。一意キーの名前は SHOW CREATE TABLE の UNIQUE KEY `email` (`email`) で確かめられます。

3つの書き方の違い

重複したときの動きを mysql クライアントの表示(Query OK, N rows affected)で比べました。

書き方 重複しなかった 重複して値が変わった 重複して値が同じ
INSERT IGNORE 1 row affected 0 rows affected, 1 warning(何も変わらない)
ON DUPLICATE KEY UPDATE 1 row affected 2 rows affected 0 rows affected
REPLACE 1 row affected 2 rows affected(削除1+挿入1)

ON DUPLICATE KEY UPDATE の「2」は、更新した1行を2と数える決まりです。アプリで影響行数を見て「挿入したか更新したか」を分けるなら、3通りを扱います。

INSERT IGNORE は重複以外も握りつぶす

INSERT IGNORE INTO users (email, name) VALUES ('a@example.com', 'A3');
Query OK, 0 rows affected, 1 warning
SHOW WARNINGS;
| Warning | 1062 | Duplicate entry 'a@example.com' for key 'users.email' |

警告に変わるのは重複だけではありません。NOT NULL の列に NULL を入れても、エラーにならずに行が入りました。

INSERT IGNORE INTO users (email, name) VALUES ('n@example.com', NULL);
SHOW WARNINGS;
| Warning | 1048 | Column 'name' cannot be null |

SELECT * FROM users WHERE email = 'n@example.com';
| id | email         | name | cnt |
|  5 | n@example.com |      |   0 |

name には NULL の代わりに空文字が入っています。IGNORE を付けない INSERT なら ERROR 1048 (23000): Column 'name' cannot be null で止まります。重複を無視するつもりで付けた IGNORE が、データの不備まで通してしまいます。使ったら SHOW WARNINGS で警告の中身を確かめます。

ON DUPLICATE KEY UPDATE は VALUES() ではなく別名で書く

よく見かける VALUES(列) の書き方は、8.4 でも動きますが警告が出ます。

INSERT INTO users (email, name) VALUES ('a@example.com', 'A4')
  ON DUPLICATE KEY UPDATE name = VALUES(name), cnt = cnt + 1;
SHOW WARNINGS;
| Warning | 1287 | 'VALUES function' is deprecated and will be removed in a future release. Please use an alias (INSERT INTO ... VALUES (...) AS alias) and replace VALUES(col) in the ON DUPLICATE KEY UPDATE clause with alias.col instead |

挿入しようとした行に AS で別名を付け、別名.列 で参照します。

INSERT INTO users (email, name) VALUES ('a@example.com', 'A5') AS new
  ON DUPLICATE KEY UPDATE name = new.name, cnt = cnt + 1;

cnt = cnt + 1 の cnt は既存の行の値です。重複するたびに数える使い方ができます。

REPLACE は行を入れ直す

REPLACE は、重複した既存の行を削除してから新しい行を入れます。

-- 実行前: id=2, email='b@example.com', name='B', cnt=0
REPLACE INTO users (email, name) VALUES ('b@example.com', 'B2');
Query OK, 2 rows affected

SELECT * FROM users WHERE email = 'b@example.com';
| id | email         | name | cnt |
|  7 | b@example.com | B2   |   0 |

id が 2 から 7 に変わりました。指定しなかった列(cnt)は元の値ではなく既定値になります。外部キーで参照されている行や、id をほかの表に保存している行に REPLACE を使うと、参照が切れます。値の一部だけを変えたいなら ON DUPLICATE KEY UPDATE を使います。

失敗した INSERT でも AUTO_INCREMENT は進む

前の節までの操作(a・b を挿入 → IGNORE → ON DUPLICATE KEY UPDATE 3回 → REPLACE 2回)を続けて流した後の id です。

SELECT id, email FROM users ORDER BY id;
|  1 | a@example.com |
|  4 | c@example.com |
|  7 | b@example.com |
|  8 | d@example.com |

2 は REPLACE で削除された元の行です。3・5・6 は、重複して何も入らなかった INSERT IGNORE と、更新で終わった ON DUPLICATE KEY UPDATE 2回が使った値です。行が入らなくても AUTO_INCREMENT の値は1つ使われます(innodb_autoinc_lock_mode は既定の 2)。同じ表を作り直して試した回では、1062 で失敗した普通の INSERT でも値が1つ進みました(2行入れた後に1回失敗させると AUTO_INCREMENT が 4)。id が連番であることを前提にした処理は、重複が起きる表では成り立ちません。

MySQL をもっと深く知るための本

MySQL徹底入門 第5版 MySQL 8.4 LTS対応

翔泳社・2025年6月・MySQL 8.4 LTS 対応

MySQL 8.4 に対応した入門書。日本MySQLユーザ会のメンバーが執筆している。

Amazonで見る

MySQL運用・管理[実践]入門 〜安全かつ高速にデータを扱う内部構造・動作原理を学ぶ

技術評論社・2024年5月

内部構造と動作原理から、運用・管理の考え方を学ぶ。

Amazonで見る

Amazonのアソシエイトとして、FIRE SE は適格販売により収入を得ています。