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?

DigitalWM
quelle
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 () .

aebersold
quelle
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.

manisch
quelle
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();
Gras Double
quelle
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();
Teodor Talov
quelle
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
17

Sie können verwenden :

ModelName::inRandomOrder()->first();
simhumileco
quelle
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

Manuel Azar
quelle
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()'));
Bilal Gultekin
quelle
3
Dies funktioniert auch und ist ein wenig sauberer. -> order_by (\ DB :: raw ('RAND ()'))
Alex Naspo
3

Verwenden Sie die Laravel-Funktion

ModelName::inRandomOrder()->first();
Kamlesh Paul
quelle
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();
Hosein Azimi
quelle
3

Ich ziehe es vor, zuerst anzugeben oder fehlzuschlagen:

$collection = YourModelName::inRandomOrder()
  ->firstOrFail();
Giovannipds
quelle
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 .

Fast Pitt
quelle
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();
Neto
quelle
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).

ctf0
quelle
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(); 
Krzysztof Chełchowski
quelle
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