laravel Eloquent chunk() 缺少一半的结果
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32700537/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Eloquent chunk() missing half the results
提问by Didier Sampaolo
I have a problem with Laravel's ORM Eloquent chunk() method. It misses some results. Here is a test query :
我对 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;
It gives 124838 results.
它给出了 124838 个结果。
But :
但 :
$num_dest = Destinataire::where('statut', '<', 3)
->where('tokenized_at', '<', $date_active)
->count();
echo $num_dest;
gives 249676, so just TWICE as the first code example.
给出 249676,所以只是 TWICE 作为第一个代码示例。
My script is supposed to edit all matching records in the database. If I launch it multiple times, it just hands out half the remaining records, each time.
我的脚本应该编辑数据库中的所有匹配记录。如果我多次启动它,它每次只分发剩余记录的一半。
I tried with DB::table() instead of the Model. I tried to add a ->take(20000) but it doesn't seem to be taken into account. I echoed the query with ->toSql() and eveything seems to be fine (the LIMIT clause is added when I add the ->take() parameter).
我尝试使用 DB::table() 而不是模型。我试图添加一个 ->take(20000) 但它似乎没有被考虑在内。我用 ->toSql() 回显了查询,一切似乎都很好(添加 ->take() 参数时添加了 LIMIT 子句)。
Any suggestions ?
有什么建议 ?
采纳答案by user1878906
Quick answer:Use chunkById()
instead of chunk()
.
快速回答:使用chunkById()
代替chunk()
。
The explanationcan be found in the Laravel documentation:
可以在Laravel 文档中找到解释:
When updating or deleting records inside the chunk callback, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the chunked results.
在块回调中更新或删除记录时,对主键或外键的任何更改都可能影响块查询。这可能会导致记录未包含在分块结果中。
Here is the solution example:
这是解决方案示例:
DB::table('users')->where('active', false)
->chunkById(100, function ($users) {
foreach ($users as $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
}
});
If you are updating database records while chunking results, your chunk results could change in unexpected ways. So, when updating records while chunking, it is always best to use the chunkById method instead. This method will automatically paginate the results based on the record's primary key.
如果您在对结果进行分块时更新数据库记录,您的分块结果可能会以意想不到的方式发生变化。因此,在分块时更新记录时,最好使用 chunkById 方法。此方法将根据记录的主键自动对结果进行分页。
(end of the update)
(更新结束)
The original answer:
原答案:
I had the same problem - only half of the total results were passed to the callback function of the chunk()method.
我遇到了同样的问题 - 只有总结果的一半传递给了chunk()方法的回调函数。
Here is the code which had problems:
这是有问题的代码:
Transaction::whereNull('processed')->chunk(100, function ($transactions) {
$transactions->each(function($transaction){
$transaction->process();
});
});
I used Laravel 5.4 and managed to solve the problem replacing the chunk()method with cursor()method and changing the code accordingly:
我使用 Laravel 5.4 并设法解决了用cursor()方法替换chunk ()方法并相应地更改代码的问题:
foreach (Transaction::whereNull('processed')->cursor() as $transaction) {
$transaction->process();
}
Even though the answer doesn't address the problem itself, it provides a valuable solution.
尽管答案不能解决问题本身,但它提供了一个有价值的解决方案。
回答by MisaGH
Imagine you are using chunk method to delete all of the records. The table has 2,000,000 records and you are going to delete all of them by 1000 chunks.
想象一下,您正在使用块方法删除所有记录。该表有 2,000,000 条记录,您将按 1000 个块删除所有记录。
$query->orderBy('id')->chunk(1000, function ($items) {
foreach($items as $item) {
$item->delete();
}
});
It will delete the first 1000 records by getting first 1000 records in a query like this:
它将通过在这样的查询中获取前 1000 条记录来删除前 1000 条记录:
SELECT * FROM table ORDER BY id LIMIT 0,1000
And then the other query from chunk method is:
然后来自块方法的另一个查询是:
SELECT * FROM table ORDER BY id LIMIT 1000,2000
Our problem is here, that we delete 1000 records and then getting results from 1000 to 2000. Actually we are missing first 1000 records and this means that we are not going to delete 1000 records in first step of chunk! This scenario will be the same for other steps. In each step we are going to miss 1000 records and this is the reason that we are not getting best result in these situations.
我们的问题在这里,我们删除 1000 条记录,然后得到 1000 到 2000 条的结果。实际上我们缺少前 1000 条记录,这意味着我们不会在块的第一步中删除 1000 条记录!对于其他步骤,此场景将相同。在每一步中,我们都会错过 1000 条记录,这就是我们在这些情况下无法获得最佳结果的原因。
I made an example for deletion because this way we could know the exact behavior of chunk method.
我做了一个删除的例子,因为这样我们可以知道块方法的确切行为。
UPDATE:
更新:
You can use chunkById()
for deleting safely.
您可以chunkById()
用于安全删除。
Read more here:
在此处阅读更多信息:
http://laravel.at.jeffsbox.eu/laravel-5-eloquent-builder-chunk-chunkbyidhttps://laravel.com/api/5.4/Illuminate/Database/Eloquent/Builder.html#method_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
回答by Stan Smulders
For anyone looking for a bit of code that solves this, here you go:
对于任何正在寻找解决此问题的代码的人,请访问:
while (Model::where('x', '>', 'y')->count() > 0)
{
Model::where('x', '>', 'y')->chunk(10, function ($models)
{
foreach ($models as $model)
{
$model->delete();
}
});
}
The problem is in the deletion / removal of the model while chunking away at the total. Including it in a while loop makes sure you get them all! This example works when deleting Models, change the while
condition to suit your needs!
问题在于删除/删除模型,同时对总数进行分块。将其包含在 while 循环中可确保您获得全部!此示例在删除模型时有效,更改while
条件以满足您的需求!