Laravel - Eloquente oder fließende zufällige Reihe


242

Wie kann ich eine zufällige Zeile mit Eloquent oder Fluent in Laravel auswählen?

Ich weiß, dass Sie mit SQL nach RAND () bestellen können. Ich möchte jedoch die zufällige Zeile erhalten, ohne die Anzahl der Datensätze vor der ersten Abfrage zu zählen.

Irgendwelche Ideen?


Es gibt keinen besten Weg, dies zu tun, ohne mindestens zwei Abfragen auszuführen.
NARKOZ

Antworten:


584

Laravel> = 5,2:

User::all()->random();
User::all()->random(10); // The amount of items you wish to receive

oder

User::inRandomOrder()->get();

oder um die spezifische Anzahl von Datensätzen zu erhalten

//5 indicates the number of records
User::inRandomOrder()->limit(5)->get();

Laravel 4.2.7 - 5.1:

User::orderByRaw("RAND()")->get();

Laravel 4.0 - 4.2.6:

User::orderBy(DB::raw('RAND()'))->get();

Laravel 3:

User::order_by(DB::raw('RAND()'))->get();

Überprüfen Sie diesen Artikel über zufällige MySQL-Zeilen. Laravel 5.2 unterstützt dies. Für ältere Versionen gibt es keine bessere Lösung als die Verwendung von RAW-Abfragen .

edit 1: Wie von Double Gras erwähnt, erlaubt orderBy () seit dieser Änderung nichts anderes als ASC oder DESC . Ich habe meine Antwort entsprechend aktualisiert.

edit 2: Laravel 5.2 implementiert hierfür endlich eine Wrapper-Funktion . Es heißt inRandomOrder () .


81
Ersetzen Sie 'get' durch 'first', wenn Sie eine einzelne Zeile möchten.
Collin Price

14
für PostgreSQL verwenden'RANDOM()'
dwenaus

2
Warnung: Bei großen Datenmengen ist dies sehr langsam und fügt für mich ungefähr 900 ms hinzu
S ..

3
Können wir das paginieren?
Irfandi D. Vendy

3
Sie können jedoch die Sortierung auf jeder neuen Seite zufällig vornehmen. Das macht keinen Sinn, weil es im Wesentlichen dasselbe ist, wie wenn Sie F5 drücken.
aebersold

49

Das funktioniert gut,

$model=Model::all()->random(1)->first();

Sie können das Argument auch in einer Zufallsfunktion ändern, um mehr als einen Datensatz zu erhalten.

Hinweis: Nicht empfohlen, wenn Sie über große Datenmengen verfügen, da hierdurch zuerst alle Zeilen abgerufen werden und dann ein zufälliger Wert zurückgegeben wird.


61
Ein leistungsbedingter Nachteil ist, dass alle Datensätze abgerufen werden.
Gras Double

3
Hier wird zufällig für das Sammlungsobjekt aufgerufen, nicht für die SQL-Abfrage. Die Zufallsfunktion wird auf der PHP-Seite ausgeführt
Astroanu

@astroanu Richtig, aber um diese Sammlung zu füllen, werden alle Zeilen abgefragt.
MetalFrog

1
Ich könnte mich irren, aber dies scheint nicht zu funktionieren, wenn der an die Zufallsfunktion übergebene Parameter der Größe der Sammlung entspricht.
Brynn Bateman

Das ist nicht gut ... Auf diese Weise rufen Sie alle Datensätze ab und erhalten einen zufälligen. Wenn Ihre Tabelle zu viele Datensätze enthält, kann dies für Ihre App schlecht sein.
Anderson Silva

34

tl; dr: Es ist heutzutage in Laravel implementiert, siehe "edit 3" unten.


Leider gibt es ab heute einige Einschränkungen bei der ->orderBy(DB::raw('RAND()'))vorgeschlagenen Lösung:

  • Es ist nicht DB-agnostisch. zB SQLite und PostgreSQL verwendenRANDOM()
  • Schlimmer noch, diese Lösung ist seit dieser Änderung nicht mehr anwendbar :

    $direction = strtolower($direction) == 'asc' ? 'asc' : 'desc';


edit: Jetzt können Sie die orderByRaw () -Methode verwenden : ->orderByRaw('RAND()'). Dies ist jedoch immer noch nicht DB-agnostisch.

FWIW, CodeIgniter implementiert eine spezielle RANDOMSortierrichtung, die beim Erstellen von Abfragen durch die richtige Grammatik ersetzt wird. Es scheint auch ziemlich einfach zu implementieren zu sein. Sieht so aus, als hätten wir einen Kandidaten für die Verbesserung von Laravel :)

Update: Hier ist das Problem auf GitHub und meine ausstehende Pull-Anfrage .


edit 2: Lass uns die Jagd abschneiden. Seit Laravel 5.1.18 können Sie dem Abfrage-Generator Makros hinzufügen:

use Illuminate\Database\Query\Builder;

Builder::macro('orderByRandom', function () {

    $randomFunctions = [
        'mysql'  => 'RAND()',
        'pgsql'  => 'RANDOM()',
        'sqlite' => 'RANDOM()',
        'sqlsrv' => 'NEWID()',
    ];

    $driver = $this->getConnection()->getDriverName();

    return $this->orderByRaw($randomFunctions[$driver]);
});

Verwendung:

User::where('active', 1)->orderByRandom()->limit(10)->get();

DB::table('users')->where('active', 1)->orderByRandom()->limit(10)->get();


edit 3: Endlich! Seit Laravel 5.2.33 ( Changelog , PR # 13642 ) können Sie die native Methode verwenden inRandomOrder():

User::where('active', 1)->inRandomOrder()->limit(10)->get();

DB::table('users')->where('active', 1)->inRandomOrder()->limit(10)->get();

Sie sollten den Makronamen 5.1 in inRandomOrder ändern, damit er vorwärtskompatibel ist;) Details, Details :)
Sander Visser

Genau das habe ich bei der Vorbereitung eines 5.1-Projekts vor der Migration auf 5.2 getan.
Gras Double

Das ist so eine tolle Antwort. Wenn ich eine Antwort finden könnte, würde ich!
Mwallisch

18

In Laravel 4 und 5 , die order_byersetzt wird durchorderBy

So sollte es sein:

User::orderBy(DB::raw('RAND()'))->get();

User :: orderBy (DB :: raw ('RAND ()')) -> get ();
Darius M.

1
Es funktioniert danke, aber können Sie einige Informationen geben, wie dies funktioniert?
Alayli

Können Sie etwas genauer sein? Welche Art von Informationen?
Teodor Talov


9

Für Laravel 5.2> =

Verwenden Sie die eloquente Methode:

inRandomOrder()

Die inRandomOrder-Methode kann verwendet werden, um die Abfrageergebnisse zufällig zu sortieren. Mit dieser Methode können Sie beispielsweise einen zufälligen Benutzer abrufen:

$randomUser = DB::table('users')
            ->inRandomOrder()
            ->first();

aus Dokumenten: https://laravel.com/docs/5.2/queries#ordering-grouping-limit-and-offset


Course :: inRandomOrder () -> take (20) -> get (); Funktioniert nicht für mich - schlechte Sortierspezifikation in Find.php Zeile 219
MJ

1
Dies ist nützlich für Modellfabriken oder DB-Seeding
Saleh Mahmood

8

Sie können die order_by-Methode auch fließend und eloquent verwenden, z.

Posts::where_status(1)->order_by(DB::raw(''),DB::raw('RAND()')); 

Dies ist eine etwas seltsame Verwendung, funktioniert aber.

Bearbeiten: Wie @Alex sagte, ist diese Verwendung sauberer und funktioniert auch:

Posts::where_status(1)->order_by(DB::raw('RAND()'));

3
Dies funktioniert auch und ist ein wenig sauberer. -> order_by (\ DB :: raw ('RAND ()'))
Alex Naspo


3

Sie können diesen Befehl ganz einfach verwenden:

// Frage: Name des Modells
// 10 Zeilen aus DB nehmen In Shuffle-Datensätzen ...

$questions = Question::orderByRaw('RAND()')->take(10)->get();

3

Ich ziehe es vor, zuerst anzugeben oder fehlzuschlagen:

$collection = YourModelName::inRandomOrder()
  ->firstOrFail();

3

Laravel verfügt über eine integrierte Methode, um die Reihenfolge der Ergebnisse zu mischen.

Hier ist ein Zitat aus der Dokumentation:

shuffle()

Die Shuffle-Methode mischt die Elemente in der Sammlung nach dem Zufallsprinzip:

$collection = collect([1, 2, 3, 4, 5]);

$shuffled = $collection->shuffle();

$shuffled->all();

// [3, 2, 5, 1, 4] - (generated randomly)

Die Dokumentation finden Sie hier .


2

Fügen Sie bei Ihrem Modell Folgendes hinzu:

public function scopeRandomize($query, $limit = 3, $exclude = [])
{
    $query = $query->whereRaw('RAND()<(SELECT ((?/COUNT(*))*10) FROM `products`)', [$limit])->orderByRaw('RAND()')->limit($limit);
    if (!empty($exclude)) {
        $query = $query->whereNotIn('id', $exclude);
    }
    return $query;
}

dann an der Route / Steuerung

$data = YourModel::randomize(8)->get();

2

Es gibt auch whereRaw('RAND()')welche das gleiche tut, können Sie dann Kette ->get()oder ->first()oder sogar verrückt werden und fügen ->paginate(int).


0

Ich habe einen Tisch mit Tausenden von Datensätzen, also brauche ich etwas schnelles. Dies ist mein Code für Pseudozufallszeilen:

// count all rows with flag active = 1
$count = MyModel::where('active', '=', '1')->count(); 

// get random id
$random_id = rand(1, $count - 1);  

// get first record after random id
$data = MyModel::where('active', '=', '1')->where('id', '>', $random_id)->take(1)->first(); 

Das Problem dabei ist, dass, wenn es mehrere Zeilen mit IDs gibt, die größer sind als $countnur die erste, jemals abgerufen wird, und es daher auch wahrscheinlicher ist, dass sie abgerufen werden als jede andere Zeile.
Kemika
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.