Красноречивый кусок() не хватает половины результатов
У меня проблема с методом Laravel ORM Eloquent chunk().
Он пропустил некоторые результаты.
Вот тестовый запрос:
$destinataires = Destinataire::where('statut', '<', 3)
->where('tokenized_at', '<', $date_active)
->chunk($this->chunk, function ($destinataires) {
foreach($destinataires as $destinataire) {
$this->i++;
}
}
echo $this->i;
Он дает 124838 результатов.
Но:
$num_dest = Destinataire::where('statut', '<', 3)
->where('tokenized_at', '<', $date_active)
->count();
echo $num_dest;
дает 249676, поэтому только ДВАЖДЫ в качестве первого примера кода.
My script должен редактировать все соответствующие записи в базе данных. Если я запускаю его несколько раз, он просто раздаёт половину оставшихся записей каждый раз.
Я попытался использовать DB:: table() вместо Модели.
Я попытался добавить a → take (20000), но, похоже, это не учитывается.
Я повторил запрос с помощью → toSql(), и eveything кажется прекрасным (предложение LIMIT добавляется, когда я добавляю параметр → take()).
Любые предложения?
Ответы
Ответ 1
У меня была та же проблема - только половина общих результатов была передана функции обратного вызова метода chunk().
Вот код, который имел проблемы:
Transaction::whereNull('processed')->chunk(100, function ($transactions) {
$transactions->each(function($transaction){
$transaction->process();
});
});
Я использовал Laravel 5.4 и смог решить проблему, заменив метод chunk() методом курсора() и соответствующим образом изменив код:
foreach (Transaction::whereNull('processed')->cursor() as $transaction) {
$transaction->process();
}
Несмотря на то, что ответ не касается самой проблемы, он обеспечивает ценное решение.
Ответ 2
Мне не удалось решить эту проблему, мне пришлось перестать использовать chunk().
Вот что я сделал, чтобы использовать простой старый PDO.
Остерегайтесь одной вещи: переменная $row, заполненная PDO, является (obvisouly) не Eloquent Model, а вместо этого просто объектом stdClass.
$pdo = \DB::connection()->getPdo();
$query = "SELECT * FROM $myModelTable WHERE ....";
$stmt = $pdo->prepare($query);
$stmt->execute();
while ($row = $stmt->fetchObject()) {
// $row is stdClass instead of MyModel
}
Надеюсь, это поможет. Я с нетерпением жду некоторой информации об этом случайном странном поведении.
Ответ 3
Представьте, что вы используете метод чанка для удаления всех записей. Таблица содержит 2 000 000 записей, и вы собираетесь удалить все из них на 1000 фрагментов.
$query->orderBy('id')->chunk(1000, function ($items) {
foreach($items as $item) {
$item->delete();
}
});
Он удалит первые 1000 записей, получив первые 1000 записей в запросе следующим образом:
SELECT * FROM table ORDER BY id LIMIT 0,1000
И тогда другой запрос из метода чанка:
SELECT * FROM table ORDER BY id LIMIT 1000,2000
Наша проблема здесь в том, что мы удаляем 1000 записей, а затем получаем результаты от 1000 до 2000. На самом деле мы пропускаем первые 1000 записей, и это означает, что мы не собираемся удалять 1000 записей на первом этапе блока! Этот сценарий будет таким же для других шагов. На каждом этапе мы будем пропускать 1000 записей, и это причина того, что мы не получаем наилучшего результата в таких ситуациях.
Я сделал пример для удаления, потому что таким образом мы могли знать точное поведение метода чанка.
ОБНОВИТЬ:
Вы можете использовать chunkById()
для безопасного удаления.
Узнайте больше здесь:
http://laravel.at.jeffsbox.eu/laravel-5-eloquent-builder-chunk-chunkbyid https://laravel.com/api/5.4/Illuminate/Database/Eloquent/Builder.html#method_chunkById
Ответ 4
Для тех, кто ищет немного кода, который решает это, вы здесь:
while (Model::where('x', '>', 'y')->count() > 0)
{
Model::where('x', '>', 'y')->chunk(10, function ($models)
{
foreach ($models as $model)
{
$model->delete();
}
});
}
Проблема заключается в удалении/удалении модели, в то время как она отрывается от общей суммы. Включая его в цикл while, вы обязательно получите их все! Этот пример работает при удалении моделей, измените условие while
в соответствии с вашими потребностями!
Ответ 5
Вы должны использовать chunkById вместо чанка