このブログは未経験からのエンジニア転職やプログラミングの勉強方法などを発信する、現役エンジニアによるエンジニアのためのトータル技術ブログです。ブログとご一緒にSNSのフォローもよろしくお願いします。

Twitterをフォローする

【Laravel】複数データを一括で更新【bulk update】

Laravel
スポンサーリンク

こんにちは、あっきー(IwswAkht)です。

今回はLaravelで複数データを一括で更新する方法、通称bulk updateについて書いていこうかと思います。

技術選定は以下になります。(MySQLとPostgreSQL両方で動作確認しますので各バージョンを記載しています)

  • PHP 7.3
  • Laravel 8.8.0
  • MySQL 8.0.1
  • PostgreSQL 13.1
スポンサーリンク

【概要】bulk updateとは

そもそもbulk updateってなんやねん。という方へ向けて簡単に説明していきます。
そんなことは知っているよって方はこちらは読み飛ばしてください。

通常Laravelでアプリケーションを開発していてデータベースへの更新処理をかけるとしたら以下のような実装になることが多いかと思います。

Laravel

ですが、例えば画面にチェックボックス並んでいてチェックした値に全部に更新を加えるという場合はどのように実装するべきでしょうか?

以下のような実装をイメージする人も少なくはないのではないでしょうか。

Laravel

データが数件とかの更新でしたらこれでも問題はないかと思います。ですがチェックボックスの選択の数がめちゃくちゃ多かった場合、またはバッチ処理で数万件のレコードに対して更新処理をかける場合でしたらどうでしょう。

何百から何万回とデータベースへの更新処理がかかることになってしまいますね。

大前提で認識しておいていただきたいのですが、基本的にデータベースへアクセスするのは重たい処理です

その重たい処理を何万回と繰り返したら速度が遅くなることは容易に想像できますよね。

そこで登場するのがbulk updateです。

レコード毎に更新値を設定→アップデート。でなく更新値を一括でまとめる→アップデート。一言で言ってしまえばこれがbulk updateの正体です。

環境によって差異は出るとは思いますが、実際に1万件のデータを一件ずつ更新した時とbulk updateで更新をしたの速度の違いとしては、1件ずつの更新には約1分ほどかかり、bulk updateの更新では15秒ほどで更新が完了しました

bulk updateの威力をなんとなく感じていただけたでしょうか。それでは実際の実装の仕方を確認していきましょう。

bulk updateのSQLの書き方

Laravelでの実装を確認する前にbulk updateで一括更新するためのSQLの書き方を確認していきます。

というのもLaravelでは残念ながらEloquent ORMクエリビルダでbulk updateを実行するメソッドは用意されていないようです。
そのため、自分でSQLを組み立ててbulk updateを実行してあげる必要性があるからです。

SQLを自分で組み立てることで複雑なSQLを実行するとはできますが、SQLインジェクションなどの脆弱性を発生させてしまう原因にもなります。選定したフレームワークなどで要件を満たせないなら前提の技術選定を考え直すということもだいじです

データベース名はlaravelとし、テーブル構成は以下とします。

テーブルのデータの内容は以下とします。

以下のSQL(MySQL)を実行します。

ターミナル

PostgreSQLの場合は以下を実行します。

ターミナル

実行結果の確認

MySQLとPostgreSQL両方とも同じSQLで更新ができることが確認できました。

SQLも直訳で直感的に何をしているのかわかるかと思います。

membersテーブルからid、1と2を含むデータのnameカラムをCASE以下の条件で更新します。

条件:idが1の時はnameカラムに「id1のnameを更新」に更新、idが2の時はnameカラムを「id2のnameを更新」に更新。

実際のコードでは更新する値などを直接書くなどはないので以下のようにバインド変数を使用しプレースホルダーでのSQLの組み立てを行なっていきます。

Laravel

ちなみにCASEはもちろんid以外のカラムで指定しても問題ありません。またCASEによって複数のカラムを更新したい、複数カラムを各々の値で更新+共通の値で更新などする場合は以下のようなSQLを組み立てます。

Laravel

CASEの条件によってnamegenderカラムを指定した値に更新して更新日updated_atには共通で現在日時を設定しています。

ここまでの例を踏まえて実際にLaravelでの実装方法を書いていきます。

Laravelでbulk update

playgroundというLaravelプロジェクトを以下の手順にそって作成してください。

ターミナル

makeコマンドで生成された各種ファイルを編集します。

app/Models/Member.php

database/migrations/xxxx_xx_xx_xxxxxx_create_members_table.php

database/seeders/MemberSeeder.php

database/seeders/DatabaseSeeder.php

.env

ここまでできたらマイグレートしてテーブルを作成します。

ターミナル

データベースの構築が完了しましたら各実装を行なっていきます。

controller

今回は簡易的にルートにアクセスしたらcontrollerの内容にアップデートする実装にします。

以下の編集を加えてください。

app/Http/Controllers/MemberController.php

view

viewは変更した内容を出力だけしておきます。

memberフォルダを作成し、その配下にindex.blade.phpを作成して以下の編集を加えてください。

resources/views/member/index.blade.php

ルーティング

以下の編集を加え、ルーティングを設定してからhttp://127.0.0.1:8000/にアクセスします

routes/web.php

以下の表示結果を確認できます。

Target class [xxxController] does not exist.

この記事通りに環境構築をしますと画面にアクセスした時にこのエラーに遭遇するかと思います。

これはLaravel8リリースノートの「ルートの名前空間の向上」に記載がありますようにRouteServiceProviderの変更に伴って起きるそうです。

Laravel8系以前からバージョンアップした場合はこのエラーは発生しませんが、Laravel8系で新規プロジェクトを作成した方はルーティングを以下に変更すれば表示されます。

routes/web.php

文字列型以外でのbulk update

ここまでの実装例でのデータベース方はname:VARCHAR(255)gender:CHAR(1)と共にString型での実装です。

データベースをPostgreSQLで実装した方は、更新対象に数値型などの他のデータ型を指定すると以下のようなエラーが発生するかと思います。

SQLSTATE[42804]: Datatype mismatch: 7 ERROR: column "column_name" is of type smallint but expression is of type text LINE 1: ...

エラーの内容通りなのですが、データ型が一致しないエラーですね。ループする配列のデータでは数値型で実装しててもずっとこのエラーが発生して僕も少しハマりました。

結論としてはPHP側であれこれデータ型をキャストしても意味なくて、SQLを組み立ててる箇所で明示的にデータ型を指定してあげることで解決することができました。

先ほどのコードを参考にするならMemberController.phpの23行目を以下のように修正すればOKです。

app/Http/Controllers/MemberController.php

chunk(分割)してbulk update

データ件数によりますが、bulk updateなど大量のデータを更新する時にもうひとつ考えてあげることがあります。

それがデータをchunk(分割)してあげることです。SQLを何回も呼び出すことは通信に負荷がかかることは冒頭に言いましたが、でかいデータを更新かけるにもこれまた負荷はかかります。

なので、基本的にbulk処理を実装する時は一緒にchunk処理を実装しておくことで動作が安定すると思います。

chunkする目安はカラム数、データ件数など様々な要因で処理速度が変わるので一概に言えず速度をはかりながら最適化してください。が答えではあるのですが僕の個人の意見で言うなら大体1000〜5000件位のchunkでいつも落ち着いてる気がしますのでご参考までに。

では、実際に実装してみましょう。と言うことで先ほどのコードを以下の様にリファクタリングしてください。

app/Http/Controllers/MemberController.php

データが更新されることが確認できたらOKです。お疲れ様でした。

さいごに

さいごまで読んでいただきありがとうございます!

bulk updateは業務でも使えるテクニックかと思いますので、ぜひ参考にしていただけたらうれしいです。

本日の内容の要点をおさらいしましょう。

  • SQLは重い処理なので通信回数は減らす
  • bulk updateなどの一括処理でSQLの回数を減らす
  • でかいデータの通信も重たい
  • chunk処理で1通信のデータサイズを小さくする

bulk updateの他にもbulk insertやbulk upsertなる一括処理もありますので、その辺も次の記事のネタにしようかと思いますので引き続きよろしくお願いします。

この記事を気に入っていただけましたらTwitterdでもプログラミングに関してのツイートをリアルタイムでしていますので
ご一緒にフォローもお願いします。

IT転職ならレバテック!

僕は未経験からSESの企業にエンジニアとして転職し、その後はフリーラン、現在は受託開発企業に転職しました。

エンジニアとしていろいろな働き方を経験し、いろいろな転職サイトや転職エージェントの方にお話を伺いました。

その経験の中でだいじだと感じたことは、ITに特化した転職エージェントのサービスを利用することです。

幅広い業界に対応した転職エージェントはIT転職に特化したエージェントと比べると紹介先の数が少ないです。

そのため希望してない紹介先に転職し、思ってたのと違うとなることもあります。

そうならないためにエンジニアを目指すなら必ずIT特化の転職サービスを登録し、たくさんの選択肢の中から自分が行きたいと思う企業を探してください

僕はフリーランスでの案件探し、受託開発企業の転職の時もレバテックのかたに紹介していただきました。

とても満足できましたので皆さんもぜひ利用してみてください!

\無料のIT系転職サービス/

/優良案件がたくさん\

スポンサーリンク
あっきー

元キャバクラ店長から未経験でエンジニアに転職した異端児。

自分の経験を元に、エンジニア転職や未経験からでも挫折しないプログラミングの勉強方法の発信をしてます。

あっきーをフォローする
LaravelPHP
\良い記事だったらシェアしてね!/
スポンサーリンク
駆け出しエンジニアのつぶやき