0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Laravel Eloquentで大量レコードを取得できない

Posted at

発生した事象

Laravel Eloquentで大量レコードを取得しようと試みた。

$builder = Post::where('id', '<>' , 1);
$posts = $builder->get();
foreach ($posts as $post) {
  // 各行のデータを処理する
}

get()した時、メモリ不足でエラーになった。該当するレコードは1,000,000件以上ある。

Fatal error: Allowed memory size of *** bytes exhausted in ...

実行環境

Laravel 10.x

案1: SQLの実行と結果取得でEloquentを使うのを止めて、cursor()でデータを取得する

$builder = Post::where('id', '<>' , 1);
$cursor = DB::connection()->cursor($builder->toSql(), $builder->getBindings());
foreach ($cursor as $row) {
  // $row は stdClass
  $data = get_object_vars($row);
  // $data は [colmun_name1 => column_value1, colmun_name2 => column_value2, …] の連想配列になる
  // 各行のデータを処理する
}

欠点

  • 1行のデータはEloquentのModel(ここではPost)にならない。
  • Model に HasMany などのrelationが定義されていても、取得できない。代わりに $builder->join() $builder->select() などを駆使して関係するデータを取得したり、整理する必要がある。
  • つまり、概ねPDOStatementによる開発に近くなる。開発者のスキルと合わなくなったり、開発効率が下がったりする可能性がある。

案2: lazyById() などでカーソルページネーションする

$builder = Post::where('id', '<>' , 1)->lazyById(1000, $column = 'id');
foreach($posts as $post){
  // 各行のデータを処理する
}

上記を実行すると、複数のSELECT文を実行してデータを取得する。

SELECT * FROM posts ORDER BY id ASC LIMIT 1000
SELECT * FROM posts WHERE id > 1000 ORDER BY id ASC LIMIT 1000
SELECT * FROM posts WHERE id > 2000 ORDER BY id ASC LIMIT 1000
・・・

lazyById() その他の選択肢について、下記リンクが詳しい。

Laravel の取得系メソッド cursor() や chunk() を徹底比較する
https://qiita.com/nekohan/items/eba0816fe8c21e3cc077

欠点

  • 同一時点のデータを取得する必要がある場合、利用できない。
  • idカラムの代わりに、複合キー(2カラム以上で構成された一意キー)を指定してページネーションする方法が無い。
    • lazyById() に限らず、EloquentのModelを使うと良く起きる話。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?