雄辩的chunk()缺少了一半的结果

时间:2015-09-21 16:48:57

标签: php laravel laravel-5 eloquent

我在使用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,所以只有TWICE作为第一个代码示例。

我的脚本应该编辑数据库中的所有匹配记录。如果我多次启动它,它每次只分发剩余记录的一半。

我尝试使用DB :: table()而不是Model。 我试图添加一个 - &gt; take(20000),但它似乎没有被考虑在内。 我用 - &gt; toSql()回复了查询,并且eveything似乎很好(当我添加 - &gt; take()参数时添加了LIMIT子句。)

有什么建议吗?

3 个答案:

答案 0 :(得分:19)

想象一下,您正在使用chunk方法删除所有记录。该表有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

然后来自chunk方法的另一个查询是:

SELECT * FROM table ORDER BY id LIMIT 1000,2000

我们的问题在于,我们删除1000条记录,然后从1000到2000获取结果。实际上我们缺少前1000条记录,这意味着我们不会在大块的第一步中删除1000条记录!其他步骤的情况也是如此。在每个步骤中,我们将错过1000条记录,这就是我们在这些情况下无法获得最佳结果的原因。

我做了一个删除示例,因为这样我们就可以知道chunk方法的确切行为。

<强>更新

您可以使用chunkById()安全删除。

在这里阅读更多内容:

When to use single quotes, double quotes, and backticks in MySQL http://laravel.at.jeffsbox.eu/laravel-5-eloquent-builder-chunk-chunkbyid

答案 1 :(得分:8)

对于任何寻找解决此问题的代码的人,请转到:

while (Model::where('x', '>', 'y')->count() > 0)
{
    Model::where('x', '>', 'y')->chunk(10, function ($models)
    {
        foreach ($models as $model)
        {
            $model->delete();
        }
    });
}

问题在于删除/移除模型,同时分析总数。将它包含在while循环中确保你得到它们!此示例适用于删除模型,更改while条件以满足您的需求!

答案 2 :(得分:2)

我遇到了同样的问题 - 只有一半的总结果被传递给 chunk()方法的回调函数。

以下是有问题的代码:

Transaction::whereNull('processed')->chunk(100, function ($transactions) {
    $transactions->each(function($transaction){
        $transaction->process();
    });
});

我使用Laravel 5.4并设法解决了用 cursor()方法替换 chunk()方法并相应更改代码的问题:

foreach (Transaction::whereNull('processed')->cursor() as $transaction) {
    $transaction->process();
}

即使答案没有解决问题本身,它也提供了有价值的解决方案。