Laravelで気軽にバルクアップデートしたい


バルクアップデートとは

バッチ処理などでは、データベースに複数の行を挿入したり、更新したりしたい場合がよくあると思います。挿入に関しては、大抵のRDBMSではINSERT INTO table (...) VALUES (...), (...)のような形で簡単に実行可能ですし、Laravelでもinsert()の第1引数に連想配列の配列を渡せば可能です。

更新に関してはそう簡単ではありませんが、MySQLに限っては、ELT()FIELD()という2つの関数を使った方法で可能です。ただし、これをLaravelで実行するのはだいぶややこしいので、簡単に実行できるようにしてみました。

使い方

上記にあるサービスプロバイダクラスのファイルを、app/Providers以下に配置し、config/app.phpprovidersApp\Providers\MySqlBulkUpdateServiceProvider::classを追加、その上で、

<?php

DB::table('users')->whereNull('email_verified_at')->updateBulk([
    ['id' => 1, 'name' => 'admin1', 'email' => '[email protected]'],
    ['id' => 2, 'name' => 'admin2', 'email' => '[email protected]'],
    ['id' => 3, 'name' => 'admin3', 'email' => '[email protected]'],
]);

あるいは、

<?php

App\Models\User::whereNull('email_verified_at')->updateBulk([
    ['id' => 1, 'name' => 'admin1', 'email' => '[email protected]'],
    ['id' => 2, 'name' => 'admin2', 'email' => '[email protected]'],
    ['id' => 3, 'name' => 'admin3', 'email' => '[email protected]'],
]);

のように実行します。

詳細

第1引数で渡した配列から、id(Eloquent\Builderからの場合は、モデルに設定された主キー)あるいは第2引数で渡したカラムで検索し、残りの値を更新します。上記のQuery\Builderの例の場合は、以下のようなSQLにコンパイルされます。

UPDATE
    `users`
SET
    `name` = ELT(FIELD(`id`, ?, ?, ?), ?, ?, ?),
    `email` = ?
WHERE
    `email_verified_at` IS NULL
    AND `id` IN (?, ?, ?)

emailは全行で同じとなっているため、ELT(), FIELD()は使わない形にします。また、updateBulk()以前に追加したWHERE句も別途設定されます。

さらに、Eloquent\Builder経由の場合はupdated_at等も自動で設定されます。

Query\Builder経由の実行時に、主キーがid以外の場合、あるいはEloquent\Builderで、主キー以外で検索したい場合は、第2引数にカラム名を指定します。

DB::table('users')->updateBulk([
    ['name' => 'admin1', 'email' => '[email protected]'],
    ['name' => 'admin2', 'email' => '[email protected]'],
    ['name' => 'admin3', 'email' => '[email protected]'],
], 'email');

この記事のライセンス


この文書はCC BY(クリエイティブ・コモンズ表示4.0国際ライセンス)で公開します。