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

Did*_*olo 15 php laravel eloquent laravel-5

我对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;
Run Code Online (Sandbox Code Playgroud)

它给出124838结果.

但是:

$num_dest = Destinataire::where('statut', '<', 3)
    ->where('tokenized_at', '<', $date_active)
    ->count();
echo $num_dest;
Run Code Online (Sandbox Code Playgroud)

得到249676,所以只有TWICE作为第一个代码示例.

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

我尝试使用DB :: table()而不是Model.我试图添加 - > take(20000),但似乎没有考虑到.我用 - > toSql()回显了查询,并且eveything似乎没问题(当我添加 - > take()参数时添加了LIMIT子句).

有什么建议 ?

Mis*_*aGH 30

想象一下,您正在使用chunk方法删除所有记录.该表有2,000,000条记录,您将删除所有这些记录1000个块.

$query->orderBy('id')->chunk(1000, function ($items) {
    foreach($items as $item) {
        $item->delete();
    }
});
Run Code Online (Sandbox Code Playgroud)

它将通过在查询中获取前1000条记录来删除前1000条记录,如下所示:

SELECT * FROM table ORDER BY id LIMIT 0,1000
Run Code Online (Sandbox Code Playgroud)

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

SELECT * FROM table ORDER BY id LIMIT 1000,2000
Run Code Online (Sandbox Code Playgroud)

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

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


更新:

您可以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

  • 这是*实际*答案,并首先说明了为什么发生此问题。Didier当前接受的答案只是一个变通方法。 (2认同)
  • 对于那些在 `chunkById()` 返回并非所有结果时遇到问题的人 - 你不应该将自定义 `orderBy()` 与 `chunkById()` 一起使用,这会破坏逻辑,因为 laravel 使用以下查询 `select * 来自 some_records 其中 id &gt; ? 并且deleted_at为空,按id asc限制100` (2认同)

use*_*906 26

快速回答:使用chunkById()代替chunk()

在迭代记录时更新删除记录时,对主键或外键的任何更改都可能影响块查询。这可能会导致结果中不包含记录。

可以在Laravel 文档中找到解释

以下是解决方案示例:

DB::table('users')->where('active', false)
    ->chunkById(100, function ($users) {
        foreach ($users as $user) {
            DB::table('users')
                ->where('id', $user->id)
                ->update(['active' => true]);
        }
    });
Run Code Online (Sandbox Code Playgroud)

如果您在对结果进行分块时更新数据库记录,您的分块结果可能会以意想不到的方式发生变化。如果您计划在分块时更新检索到的记录,最好使用 chunkById 方法。此方法将根据记录的主键自动对结果进行分页。

(更新结束)

原答案:

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

这是有问题的代码:

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

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

foreach (Transaction::whereNull('processed')->cursor() as $transaction) {
    $transaction->process();
}
Run Code Online (Sandbox Code Playgroud)

尽管答案不能解决问题本身,但它提供了一个有价值的解决方案。


Sta*_*ers 8

对于任何想要解决这个问题的代码的人来说,你可以去:

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

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