Красноречивый кусок() не хватает половины результатов

У меня проблема с методом 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 вместо чанка