※この記事にはアフィリエイト広告(プロモーション)が含まれます。
主キーや一意キーと同じ値の行を入れようとすると、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 が連番であることを前提にした処理は、重複が起きる表では成り立ちません。