Как вырезать результаты из пользовательского запроса в Laravel

У меня есть пользовательский запрос, который захватывает данные из старой системы и сопоставляет их с моделями в новой системе. Запрос выглядит так:

$companies = DB::connection('legacy')->select("...");

И так как это очень много данных, я бы хотел использовать функцию Chunk Eloquent (только пример кода, скопированный из их документов):

 User::chunk(200, function($users) { foreach ($users as $user) { // } }); 

Как это реализовать?


Изменить: теперь мой код выглядит так, что не дает ответа:

 DB::connection('legacy')->select("SELECT * FROM companies")->chunk(200, function($companies) { foreach ($companies as $company) { // dd($company); $entity = Entity::firstOrNew(['external_id' => $company->companyKey]); $entity->name = $company->companyName; $entity->save(); } }); 

Попробуйте что-то вроде этого:

 <?php $max = 100; $total = DB::connection('legacy')->select("...")->count(); $pages = ceil($total / $max); for ($i = 1; $i < ($pages + 1); $i++) { $offset = (($i - 1) * $max); $start = ($offset == 0 ? 0 : ($offset + 1)); $legacy = DB::connection('legacy')->select("...")->skip($start)->take($max)->get(); /* Do stuff. */ } 

В основном дублирует то, что делает Paginator Laravel без дополнительных накладных расходов.

У ответа их есть ошибка.

КАК ЕСТЬ

если таблица «legacy» имеет столбец «id» и есть 1,2,3,4,5 … 100 пронумерованных данных.

 <?php $max = 10; $total = DB::connection('legacy')->select("...")->count(); $pages = ceil($total / $max); for ($i = 1; $i < ($pages + 1); $i++) { $offset = (($i - 1) * $max); $start = ($offset == 0 ? 0 : ($offset + 1)); $legacy = DB::connection('legacy')->select("...")->skip($start)->take($max)->get(); /* Do stuff. */ $legacyIds = $legacy->lists("id"); echo "i = " . $i . ": \n"; print_r($legacyIds); } //Result i = 1: Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 [5] => 6 [6] => 7 [7] => 8 [8] => 9 [9] => 10 ) i = 2: Array ( [0] => 12 [1] => 13 [2] => 14 [3] => 15 [4] => 16 [5] => 17 [6] => 18 [7] => 19 [8] => 20 [9] => 21 ) ... 

ДЕЛАТЬ

 $max = 10; $total = DB::connection('legacy')->select("...")->count(); $pages = ceil($total / $max); for ($i = 1; $i < ($pages + 1); $i++) { $offset = (($i - 1) * $max); $legacy = DB::connection('legacy')->select("...")->skip($offset)->take($max)->get(); /* Do stuff. */ } 

Функция chuck доступна только для моделей Eloquent и QueryBuilder, например

 DB::table('tbl')->where('num', '>', 3)->chunk(500, function($rows) { // process $rows }); 

Но он не будет работать для запроса DB::select('...') . Вам нужно либо использовать запрос QueryBuilder, либо использовать базовый объект PDO для запроса базы данных, например:

 $pdo = DB::getPdo(); $sth = $pdo->prepare("SELECT ...."); $sth->execute(); while ($row = $sth->fetch(PDO::FETCH_ASSOC)) { // ... } 

Ни один из этих ответов не работал для меня. Я создал свою собственную функцию, основанную на ответе @deyes.

 private static function chunk($query, $max, $function) { $counter = preg_replace('/SELECT (.*?) FROM/', 'SELECT COUNT(*) FROM', $query); $total = DB::connection('legacy')->select($counter)[0]; $total = (array)$total; $total = $total['COUNT(*)']; $pages = ceil($total / $max); for ($i = 1; $i < ($pages + 1); $i++) { $offset = (($i - 1) * $max); $start = ($offset == 0 ? 0 : ($offset + 1)); $items = DB::connection('legacy')->select($query . ' LIMIT ' . $offset . ', ' . $max); $function($items); unset($items); } } этот private static function chunk($query, $max, $function) { $counter = preg_replace('/SELECT (.*?) FROM/', 'SELECT COUNT(*) FROM', $query); $total = DB::connection('legacy')->select($counter)[0]; $total = (array)$total; $total = $total['COUNT(*)']; $pages = ceil($total / $max); for ($i = 1; $i < ($pages + 1); $i++) { $offset = (($i - 1) * $max); $start = ($offset == 0 ? 0 : ($offset + 1)); $items = DB::connection('legacy')->select($query . ' LIMIT ' . $offset . ', ' . $max); $function($items); unset($items); } } не private static function chunk($query, $max, $function) { $counter = preg_replace('/SELECT (.*?) FROM/', 'SELECT COUNT(*) FROM', $query); $total = DB::connection('legacy')->select($counter)[0]; $total = (array)$total; $total = $total['COUNT(*)']; $pages = ceil($total / $max); for ($i = 1; $i < ($pages + 1); $i++) { $offset = (($i - 1) * $max); $start = ($offset == 0 ? 0 : ($offset + 1)); $items = DB::connection('legacy')->select($query . ' LIMIT ' . $offset . ', ' . $max); $function($items); unset($items); } } 

Применение

 YourClass::chunk('SELECT * FROM tablename', 50, function($items) { //Work with $items. }); 

Обратите внимание, что это простое быстрое исправление и ваш запрос, вероятно, должны быть довольно простыми, поскольку я использую search-replace для создания запроса на подсчет, и я просто привязываюсь к LIMIT X, Y до конца запроса, но он работает для меня.

Нашел этот вопрос случайно, но небольшой трюк, который может пригодиться в некоторых случаях.

 $query = 'SELECT * FROM ... JOIN ... UNION ... WHATEVER ... GROUP BY'; // This is the important part: $query = '(' . $query . ') somealias'; \DB::table(\DB::raw($query))->chunk(1000, function($rows){ // Do something }); 

Выполнение запроса laravel выполняется следующим образом:

 select * from (...) somealias LIMIT ... OFFSET ... 

Это должно работать, по крайней мере, в Laravel 5.1. Но я не вижу причины, почему он не должен работать в 4+.

Edit: Также я не уверен, как вы должны установить драйвер для использования, поскольку у меня не было необходимости в этом, но я уверен, что это возможно.

Многие ответы на этой странице выполняют поиск всех записей, чтобы тренировать, сколько «страниц» есть. Это может быть медленным и не нужно, так как мы не разбиваем на страницы, которые мы чересчур. Нам нужно знать только общее количество страниц, когда мы показываем paginating, чтобы показать пользователя.

Таким образом, альтернатива получению подсчета всех записей в начале – это сделать следующее:

  $recordsRemaining = true; $lookupsCompleted = 0; $chunkSize = 200; while($recordsRemaining){ $companies = DB::connection('legacy')->select("...")->skip($chunkSize*$lookupsCompleted)->take($chunkSize)->get(); if($legacy->count() < $chunkSize){ $recordsRemaining = false; } foreach($companies as $company){ //Do something } $lookupsCompleted++; } в  $recordsRemaining = true; $lookupsCompleted = 0; $chunkSize = 200; while($recordsRemaining){ $companies = DB::connection('legacy')->select("...")->skip($chunkSize*$lookupsCompleted)->take($chunkSize)->get(); if($legacy->count() < $chunkSize){ $recordsRemaining = false; } foreach($companies as $company){ //Do something } $lookupsCompleted++; } 

Это делает то же самое, что и принятый ответ, но более эффективен.

Я считаю, что вы можете использовать chunk в построителе запросов. Например

 DB::connection('legacy')->select("...")->chunk(200, function($companies){ //do something with $companies });