INSERT構文にIGNOREキーワードを使用する
IGNOREキーワードを使用すると
UNIQUEインデックスまたはPRIMARY KEY値を複製にする行よって重複エラーが発生したとき、ステートメントは中止される。
重複する行は破棄され、エラーは発生しない。
(参考)MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.5 INSERT 構文
重複チェックの対象はUNIQUEインデックスまたはPRIMARY KEY値であり、重複チェックするフィールドを指定できない。
例えば、次のようなテーブルがあったとき、
CREATE TABLE users (
id INT AUTO_INCREMENT NOT NULL,
name VARCHAR(255) UNIQUE,
email VARCHAR(255),
PRIMARY KEY(id)
);
1回目は追加される。
INSERT IGNORE INTO users(name, email) VALUES('鈴木', 'suzuki1@example.com');
2回目は”鈴木”がすでに登録されているため、追加されない。
INSERT IGNORE INTO users(name, email) VALUES('鈴木', 'suzuki2@example.com');
// 0 row(s) affected, 1 warning(s): 1062 Duplicate entry '鈴木' for key 'name'
Laravel 5.8ではinsertOrIgnoreが追加され、次のように書ける。
DB::table('users')->insertOrIgnore([
['name' => '鈴木', 'email' => 'suzuki@example.com'],
['name' => '田中', 'email' => 'tanaka@example.com'],
]);
(参考)Database: Query Builder – Laravel – The PHP Framework For Web Artisans
なお、INSERT IGNOREがユニーク制約以外のエラーも無視するようになるので、注意が必要だ。
次のSQLはエラーになり、登録されない。
INSERT INTO users(id, name, email) VALUES('a', '渡辺', 'watanabe@example.com');
// Error Code: 1366. Incorrect integer value: 'a' for column 'id' at row 1
次のSQLはエラーにならず、登録されてしまう。
INSERT IGNORE INTO users(id, name, email) VALUES('a', '渡辺', 'watanabe@example.com');
INSERT 〜 SELECT 〜 WHERE NOT EXISTS(〜)
例えば、次のようなテーブルがあるとする。
UNIQUE制約がなくても機能するため、UNIQUE制約をつけていない。
CREATE TABLE users (
id INT AUTO_INCREMENT NOT NULL,
name VARCHAR(255),
email VARCHAR(255),
PRIMARY KEY(id)
);
nameが’鈴木’に一致するレコードがなければ、登録するSQLは次のようになる。
INSERT INTO users(name, email)
SELECT '鈴木', 'suzuki@example.com'
FROM dual
WHERE NOT EXISTS (
SELECT 1
FROM users
WHERE name = '鈴木'
)
Laravelでは次のように書く。
$select = DB::table(DB::raw('dual'))
->select([
DB::raw('鈴木'),
DB::raw('suzuki@example.com')
])
->whereNotExists(function ($query) {
$query->select(DB::raw(1))
->from('users')
->where('name', '鈴木');
});
$bindings = $select->getBindings();
$insertQuery = 'INSERT INTO users (name, email)' . $select->toSql();
DB::insert($insertQuery, $bindings);