MySQLで条件に一致するレコードがなければ新規登録、あれば何もしない

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);

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください