Eloquent chunk() fehlt die Hälfte der Ergebnisse

Lesezeit: 5 Minuten

Benutzer-Avatar
Didier Sampaolo

Ich habe ein Problem mit der ORM Eloquent chunk() Methode von Laravel. Es fehlen einige Ergebnisse. Hier eine Testabfrage:

$destinataires = Destinataire::where('statut', '<', 3)
    ->where('tokenized_at', '<', $date_active)
    ->chunk($this->chunk, function ($destinataires) {
        foreach($destinataires as $destinataire) {
            $this->i++;
        }
    }
echo $this->i;

Es gibt 124838 Ergebnisse.

Aber :

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

ergibt 249676, also nur ZWEIMAL als erstes Codebeispiel.

Mein Skript soll alle übereinstimmenden Datensätze in der Datenbank bearbeiten. Wenn ich es mehrmals starte, gibt es jedes Mal nur die Hälfte der verbleibenden Datensätze aus.

Ich habe es mit DB::table() anstelle des Modells versucht. Ich habe versucht, ein ->take(20000) hinzuzufügen, aber es scheint nicht berücksichtigt zu werden. Ich habe die Abfrage mit ->toSql() wiederholt und alles scheint in Ordnung zu sein (die LIMIT-Klausel wird hinzugefügt, wenn ich den Parameter ->take() hinzufüge).

Irgendwelche Vorschläge ?

  • Was gibt $this->chunk aus?

    – Dibesjr

    23. September 2015 um 16:58 Uhr

  • Es ist eine Ganzzahl (dh 2000), ich habe es in $chunk_size umbenannt, es hat nicht geholfen.

    – Didier Sampaolo

    24. September 2015 um 1:07 Uhr

  • Können Sie sehen, was die Ausgabe oder der Wert dieser Variablen ist?

    – Dibesjr

    24. September 2015 um 1:07 Uhr

  • Sicher: int(2000) (auch nach ein paar Iterationen)

    – Didier Sampaolo

    24. September 2015 um 1:14 Uhr

  • @adelinemr Ich habe herausgefunden, was in meinem Fall die Ursache war, aber in Ihrem Fall könnte es ein anderes Problem geben. Ich habe das Modell innerhalb der Schleife so aktualisiert, dass die ursprüngliche Abfragebedingung bei jeder Iteration unterschiedliche Ergebnisse zurückgab.

    – kyriakos

    29. Oktober 2015 um 9:05 Uhr

Benutzer-Avatar
Misagh Laghaei

Stellen Sie sich vor, Sie verwenden die Chunk-Methode, um alle Datensätze zu löschen. Die Tabelle hat 2.000.000 Datensätze und Sie werden sie alle in 1000 Blöcken löschen.

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

Die ersten 1000 Datensätze werden gelöscht, indem die ersten 1000 Datensätze in einer Abfrage wie dieser abgerufen werden:

SELECT * FROM table ORDER BY id LIMIT 0,1000

Und dann ist die andere Abfrage von der Chunk-Methode:

SELECT * FROM table ORDER BY id LIMIT 1000,2000

Unser Problem ist hier, dass wir 1000 Datensätze löschen und dann Ergebnisse von 1000 bis 2000 erhalten. Tatsächlich fehlen uns die ersten 1000 Datensätze und das bedeutet, dass wir im ersten Schritt des Chunks nicht 1000 Datensätze löschen werden! Dieses Szenario ist für andere Schritte dasselbe. In jedem Schritt werden wir 1000 Datensätze verpassen und das ist der Grund, warum wir in diesen Situationen nicht das beste Ergebnis erzielen.

Ich habe ein Beispiel für das Löschen gemacht, weil wir auf diese Weise das genaue Verhalten der Chunk-Methode kennen konnten.


AKTUALISIEREN:

Sie können verwenden chunkById() zum sicheren Löschen.

Lesen Sie hier mehr:

http://laravel.at.jeffsbox.eu/laravel-5-eloquent-builder-chunk-chunkbyid
https://laravel.com/api/5.4/Illuminate/Database/Eloquent/Builder.html#method_chunkById

  • Dies ist das tatsächlich Antworten Sie mit einer Erklärung, warum das Problem überhaupt aufgetreten ist. Die aktuell akzeptierte Antwort von Didier ist nur eine hackige Problemumgehung.

    – Sacha

    30. Oktober 2017 um 17:20 Uhr

  • TOLL! DANKE!

    – Ozan Kurt

    14. August 2018 um 8:27 Uhr

  • Für diejenigen, die sich dem Problem wann stellen werden chunkById() gibt nicht alle Ergebnisse zurück – Sie SOLLTEN NICHT benutzerdefiniert verwenden orderBy() mit chunkById()wird die Logik gebrochen, da Laravel die folgende Abfrage verwendet select * from some_records where id > ? and deleted_at is null order by id asc limit 100

    – gorodezkiy

    9. April 2019 um 19:27 Uhr


  • Gut beschrieben Misagh. Danke vielmals.

    – Seyed Amir KhalifehSoltani

    19. Januar um 7:49 Uhr


Benutzer-Avatar
svet

Schnelle Antwort: Verwenden chunkById() Anstatt von chunk().

Wann Aktualisierung oder löschen Datensätze während der Iteration über sie, können sich Änderungen am Primärschlüssel oder an den Fremdschlüsseln auf die Chunk-Abfrage auswirken. Dies könnte möglicherweise dazu führen, dass Datensätze nicht in die Ergebnisse aufgenommen werden.

Die Erklärung finden Sie in der Laravel-Dokumentation:

Hier das Lösungsbeispiel:

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

Wenn Sie Datenbankeinträge aktualisieren, während Sie Ergebnisse aufteilen, können sich Ihre Chunk-Ergebnisse auf unerwartete Weise ändern. Wenn Sie vorhaben, die abgerufenen Datensätze beim Chunking zu aktualisieren, ist es immer am besten, stattdessen die chunkById-Methode zu verwenden. Diese Methode paginiert die Ergebnisse automatisch basierend auf dem Primärschlüssel des Datensatzes.

(Ende des Updates)

Die ursprüngliche Antwort:

Ich hatte das selbe Problem – nur die Hälfte der Gesamtergebnisse wurden an die Callback-Funktion übergeben Stück () Methode.

Hier ist der Code, der Probleme hatte:

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

Ich habe Laravel 5.4 verwendet und es geschafft, das Problem zu lösen, indem ich das ersetzte Stück () Methode mit Mauszeiger() Methode und ändern Sie den Code entsprechend:

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

Auch wenn die Antwort das Problem selbst nicht anspricht, bietet sie eine wertvolle Lösung.

  • MisaGH liefert den Grund, warum dies passiert, aber ich denke, diese Cursor-Lösung bietet eine gute speichereffiziente Problemumgehung, von der ich vermute, dass sie der Grund dafür ist, dass die Leute überhaupt Chunk verwenden

    – Karlton

    13. Juli 2018 um 10:18 Uhr

Benutzer-Avatar
Stan Smulders

Für alle, die nach Code suchen, der dies löst, hier geht es:

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

Das Problem liegt in der Löschung/Entfernung des Modells bei gleichzeitiger Aufteilung auf die Gesamtheit. Wenn Sie es in eine While-Schleife einfügen, stellen Sie sicher, dass Sie alle erhalten! Dieses Beispiel funktioniert beim Löschen von Modellen, ändern Sie die while Zustand nach Ihren Wünschen!

  • Ich habe gerade eine Stunde damit verbracht, mich am Kopf zu kratzen, bevor mir klar wurde, dass das delete() in chunk() mit seiner Paginierung herumspielt, also hat mir Ihre Lösung Zeit gespart. Vielen Dank.

    – Andy

    19. März 2018 um 9:57 Uhr

1074430cookie-checkEloquent chunk() fehlt die Hälfte der Ergebnisse

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy