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をするにはサードパーティパッケージが必要