Laravel - elokwentny lub płynny losowy rząd


242

Jak mogę wybrać losowy wiersz za pomocą Eloquent lub Fluent w frameworku Laravel?

Wiem, że używając SQL, możesz zrobić porządek za pomocą RAND (). Chciałbym jednak uzyskać losowy wiersz bez liczenia liczby rekordów przed początkowym zapytaniem.

Jakieś pomysły?


Nie ma najlepszego sposobu, aby to zrobić bez wykonania co najmniej dwóch zapytań.
NARKOZ,

Odpowiedzi:


584

Laravel> = 5,2:

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

lub

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

lub uzyskać określoną liczbę rekordów

//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();

Sprawdź ten artykuł na temat losowych wierszy MySQL. Laravel 5.2 obsługuje to, w przypadku starszych wersji nie ma lepszego rozwiązania niż używanie zapytań RAW .

edycja 1: Jak wspomniano w Double Gras, orderBy () nie zezwala na nic innego niż ASC lub DESC od tej zmiany. Zaktualizowałem odpowiednio swoją odpowiedź.

edycja 2: Laravel 5.2 wreszcie implementuje w tym celu funkcję otoki . Nazywa się on inRandomOrder () .


81
Zamień „get” na „first”, jeśli chcesz mieć pojedynczy wiersz.
Collin Price

14
do użytku PostgreSQL'RANDOM()'
dwenaus

2
Ostrzeżenie: w przypadku dużych zestawów danych jest to bardzo wolne, dodając dla mnie około 900 ms
S ..

3
Czy możemy to podzielić na strony?
Irfandi D. Vendy

3
Możesz jednak sortować losowo na każdej nowej stronie. To nie ma sensu, ponieważ jest to w zasadzie to samo, co naciśnięcie F5.
aebersold

49

To działa dobrze,

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

możesz także zmienić argument w funkcji losowej, aby uzyskać więcej niż jeden rekord.

Uwaga: niezalecane, jeśli masz ogromne dane, ponieważ najpierw pobierze wszystkie wiersze, a następnie zwróci losową wartość.


61
Minusem wydajności jest to, że wszystkie rekordy są pobierane.
Gras Double

3
tutaj losowe jest wywoływane w obiekcie kolekcji, a nie w zapytaniu sql. funkcja losowa jest uruchamiana po stronie php
astroanu

@astroanu Racja, ale aby zapełnić tę kolekcję, wszystkie wiersze są sprawdzane.
MetalFrog

1
Mogę się mylić, ale wydaje się, że to nie działa, gdy parametr przekazany do funkcji losowej jest taki sam jak rozmiar kolekcji.
Brynn Bateman,

To nie jest dobre ... W ten sposób odzyskujesz wszystkie rekordy i dostajesz losowy. Jeśli Twoja tabela ma zbyt wiele rekordów, może to być złe dla Twojej aplikacji.
Anderson Silva,

34

tl; dr: Obecnie jest zaimplementowany w Laravel, patrz „edycja 3” poniżej.


Niestety, na dzień dzisiejszy istnieją pewne zastrzeżenia dotyczące ->orderBy(DB::raw('RAND()'))proponowanego rozwiązania:

  • To nie jest DB-agnostyk. np. użycie SQLite i PostgreSQLRANDOM()
  • Co gorsza, to rozwiązanie nie ma już zastosowania, ponieważ ta zmiana :

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


edycja: Teraz możesz użyć metody orderByRaw () :->orderByRaw('RAND()') . Jednak nadal nie jest to agnostyk DB.

FWIW, CodeIgniter implementuje specjalną RANDOM kierunek sortowania, który jest zastępowany poprawną gramatyką podczas budowania zapytania. Wydaje się również, że jest dość łatwy do wdrożenia. Wygląda na to, że mamy kandydata na ulepszenie Laravela :)

aktualizacja: tutaj jest problem na GitHub i moje oczekujące żądanie ściągnięcia .


edycja 2: Wytnijmy pościg. Od wersji Laravel 5.1.18 możesz dodawać makra do konstruktora zapytań:

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]);
});

Stosowanie:

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

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


edycja 3: Wreszcie! Od wersji Laravel 5.2.33 (dziennik zmian , PR # 13642 ) możesz używać metody natywnej inRandomOrder():

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

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

Powinieneś zmienić nazwę makra 5.1 na inRandomOrder, aby była kompatybilna z przyszłością;) szczegóły, szczegóły :)
Sander Visser

To jest dokładnie jedna rzecz, którą zrobiłem podczas przygotowywania projektu 5.1 przed migracją do wersji 5.2.
Gras Double

To świetna odpowiedź. Gdybym mógł wybrać odpowiedź, zrobiłbym to!
mwallisch

18

W Laravel 4 i 5order_by otrzymuje brzmienie :orderBy

Powinno to być:

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

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

1
Działa dzięki, ale czy możesz podać jakieś informacje, jak to działa?
alayli

Czy możesz być trochę bardziej szczegółowy? Jakie informacje?
Teodor Talov


9

Dla Laravela 5.2> =

użyj Elokwentnej metody:

inRandomOrder()

Do losowego sortowania wyników zapytania można użyć metody inRandomOrder. Na przykład możesz użyć tej metody, aby pobrać losowego użytkownika:

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

z dokumentów: https://laravel.com/docs/5.2/queries#ordering-grouping-limit-and-offset


Course :: inRandomOrder () -> take (20) -> get (); Nie działa dla mnie - zła specyfikacja sortowania w linii Find.php 219
MJ

1
Przydaje się to w fabrykach modeli lub siewie
bazy danych

8

Możesz także użyć metody order_by z płynnym i wymownym, takim jak:

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

To trochę dziwne użycie, ale działa.

Edycja: Jak powiedział @Alex, to użycie jest czystsze i działa również:

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

3
to również działa i jest trochę czystsze .. -> order_by (\ DB :: raw ('RAND ()'))
Alex Naspo 24.01.2013


3

Możesz łatwo użyć tego polecenia:

// Pytanie: nazwa modelu
// weź 10 wierszy z DB W losowych rekordach ...

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

3

Wolę określić jako pierwszy lub nie:

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

3

Laravel ma wbudowaną metodę zmiany kolejności wyników.

Oto cytat z dokumentacji:

shuffle()

Metoda losowania losowo przetasowuje elementy w kolekcji:

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

$shuffled = $collection->shuffle();

$shuffled->all();

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

Tutaj możesz zobaczyć dokumentację .


2

Dodaj do swojego modelu:

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;
}

następnie na trasie / kontrolerze

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

2

Istnieje również whereRaw('RAND()')który robi to samo, można wtedy łańcuch ->get()lub ->first()nawet zaszaleć i dodać ->paginate(int).


0

Mam tabelę z tysiącami rekordów, więc potrzebuję czegoś szybko. To jest mój kod dla pseudolosowego wiersza:

// 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(); 

Problem polega na tym, że jeśli istnieje wiele wierszy o identyfikatorach większych niż $counttylko pierwszy z nich byłby kiedykolwiek odzyskany, a zatem bardziej prawdopodobne jest, że zostanie on pobrany niż jakikolwiek inny wiersz.
kemika
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.