day14の今日は、複数のデータを一気に更新するバッチ更新のやり方を見ていきます。
Doctrine
<?php declare(strict_types=1); use App\Entity\Author; use App\Entity\Book; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\ResultSetMapping; require __DIR__.'/../vendor/autoload.php'; /** @var EntityManagerInterface $entityManager */ $entityManager = require __DIR__.'/bootstrap.php'; // エンティティを使ってやる for ($i = 1; $i <= 100; $i++) { $book = new Book(); $book->setTitle(sprintf('title%d', $i)); $book->setPrice(100 * $i); $entityManager->persist($book); } $entityManager->flush(); // begin + 100件のinsert + commitがここで行われる // エンティティを使えないぐらい多い件数を扱う場合 $conn = $entityManager->getConnection(); $conn->beginTransaction(); for ($i = 1; $i <= 1000000; $i++) { $conn->insert('books', [ 'title' => sprintf('title%d', $i), 'price' => $i * 10, ]); } $conn->commit(); // updateもできる $conn->beginTransaction(); for ($i = 1; $i <= 1000000; $i++) { $conn->update('books', [ 'price' => $i * 20, ], ['id' => $i]); } $conn->commit();
- 通常のエンティティを用いた保存でもflush()を呼ぶまでinsertやupdateは発行されないので、バッチ更新的になる。
- エンティティはPHPのオブジェクトなので、大量にインスタンスを作るとPHPのメモリを消費しすぎる。それを避けるためにEntityManagerから
getConnection()
を使ってconnectionを取り出してinsert, updateを行うこともできる。(EntityManager::getConnection()の返り値はDoctrine\DBAL\Connection
)
Eloquent
<?php declare(strict_types=1); use App\Models\Book; use Illuminate\Support\Facades\DB; require __DIR__.'/../vendor/autoload.php'; require __DIR__.'/bootstrap.php'; // insert DB::beginTransaction(); for ($i = 1; $i <= 100000; $i++) { Book::query()->insert(['title' => sprintf('title%d', $i), 'price' => $i * 100]); } DB::commit(); // update DB::beginTransaction(); for ($i = 1; $i <= 100000; $i++) { DB::update( 'UPDATE books SET title = ?, price = ? WHERE id = ?', [ sprintf('title%d', $i), $i * 200, $i, ] ); } DB::commit();
- モデルを用いてバッチinsert, バッチupdateをするにはサードパーティパッケージが必要