Laravel - Baris acak Fasih atau Lancar


242

Bagaimana saya bisa memilih baris acak menggunakan Eloquent atau Fluent in Laravel framework?

Saya tahu bahwa dengan menggunakan SQL, Anda dapat melakukan pemesanan dengan RAND (). Namun, saya ingin mendapatkan baris acak tanpa menghitung jumlah rekaman sebelum kueri awal.

Ada ide?


Tidak ada cara terbaik untuk melakukan ini tanpa menjalankan setidaknya dua permintaan.
NARKOZ

Jawaban:


587

Laravel> = 5.2:

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

atau

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

atau untuk mendapatkan jumlah catatan spesifik

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

Lihat artikel ini di baris acak MySQL. Laravel 5.2 mendukung ini, untuk versi yang lebih lama, tidak ada solusi yang lebih baik daripada menggunakan RAW Queries .

sunting 1: Seperti yang disebutkan oleh Double Gras, orderBy () tidak mengizinkan apa pun selain ASC atau DESC sejak perubahan ini . Saya memperbarui jawaban saya sesuai.

sunting 2: Laravel 5.2 akhirnya mengimplementasikan fungsi wrapper untuk ini. Ini disebut inRandomOrder () .


81
Ganti 'dapatkan' dengan 'pertama' jika Anda ingin satu baris.
Harga Collin

14
untuk penggunaan PostgreSQL'RANDOM()'
dwenaus

2
Peringatan: pada dataset besar ini sangat lambat, menambahkan sekitar 900 ms untuk saya
S ..

3
Bisakah kita membuat paginasi ini?
Irfandi D. Vendy

3
Anda bisa, namun penyortirannya akan acak pada setiap halaman baru. Yang tidak masuk akal karena pada dasarnya sama dengan Anda menekan F5.
aebersold

49

Ini berfungsi dengan baik,

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

Anda juga dapat mengubah argumen dalam fungsi acak untuk mendapatkan lebih dari satu catatan.

Catatan: tidak disarankan jika Anda memiliki data besar karena ini akan mengambil semua baris terlebih dahulu dan kemudian mengembalikan nilai acak.


61
Kelemahan kinerja-bijaksana adalah bahwa semua catatan diambil.
Gras Double

3
di sini acak dipanggil pada objek koleksi bukan kueri sql. fungsi acak dijalankan di sisi php
astroanu

@astroanu Benar, tetapi untuk mengisi koleksi itu, semua baris dipertanyakan.
MetalFrog

1
Saya bisa saja salah, tetapi ini tampaknya tidak berfungsi ketika parameter yang dilewatkan ke fungsi acak sama dengan ukuran koleksi.
Brynn Bateman

Ini tidak baik ... Dengan cara ini Anda mengambil semua catatan dan mendapatkan yang acak. Jika tabel Anda memiliki terlalu banyak catatan, ini bisa berakibat buruk untuk aplikasi Anda.
Anderson Silva

34

tl; dr: Saat ini diterapkan ke Laravel, lihat "sunting 3" di bawah.


Sayangnya, sampai hari ini ada beberapa peringatan dengan ->orderBy(DB::raw('RAND()'))solusi yang diusulkan:

  • Itu bukan DB-agnostik. misalnya penggunaan SQLite dan PostgreSQLRANDOM()
  • Lebih buruk lagi, solusi ini tidak berlaku lagi sejak perubahan ini :

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


sunting: Sekarang Anda dapat menggunakan metode orderByRaw () :->orderByRaw('RAND()') . Namun ini masih bukan DB-agnostik.

FWIW, CodeIgniter mengimplementasikan khusus RANDOM arah penyortiran , yang diganti dengan tata bahasa yang benar ketika membangun kueri. Juga tampaknya cukup mudah diimplementasikan. Sepertinya kami memiliki kandidat untuk meningkatkan Laravel :)

pembaruan: inilah masalah tentang ini di GitHub, dan permintaan tarik saya yang tertunda .


edit 2: Mari kita memotong pengejaran. Karena Laravel 5.1.18 Anda dapat menambahkan makro ke pembuat kueri:

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

Pemakaian:

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

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


edit 3: Akhirnya! Karena Laravel 5.2.33 ( changelog , PR # 13642 ) Anda dapat menggunakan metode asli inRandomOrder():

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

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

Anda harus mengubah nama makro 5.1 menjadi inRandomOrder sehingga kompatibel;) detail, detail :)
Sander Visser

Itulah satu hal yang saya lakukan ketika menyiapkan proyek 5.1 sebelum memindahkannya ke 5.2.
Gras Double

Ini jawaban yang bagus. Jika saya dapat menemukan jawaban, saya akan melakukannya!
mwallisch

18

Dalam LARAVEL 4 dan 5 yang order_bydigantikan olehorderBy

Jadi, seharusnya:

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

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

1
Ini berfungsi, terima kasih, tetapi bisakah Anda memberikan informasi bagaimana ini bekerja?
alayli

Bisakah kamu sedikit lebih spesifik? Informasi seperti apa?
Teodor Talov


9

Untuk Laravel 5.2> =

gunakan metode Eloquent:

inRandomOrder()

Metode inRandomOrder dapat digunakan untuk mengurutkan hasil permintaan secara acak. Misalnya, Anda dapat menggunakan metode ini untuk mengambil pengguna acak:

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

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


Kursus :: inRandomOrder () -> take (20) -> get (); Tidak berfungsi untuk saya - spesifikasi pengurutan buruk di baris Find.php 219
MJ

1
Yang ini berguna untuk pabrik model atau penyemaian db
Saleh Mahmood

8

Anda juga dapat menggunakan metode order_by dengan fasih dan fasih seperti:

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

Ini sedikit aneh penggunaannya, tetapi berhasil.

Sunting: Seperti yang dikatakan @Alex, penggunaan ini lebih bersih dan juga berfungsi:

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

3
ini berfungsi juga dan sedikit lebih bersih .. -> order_by (\ DB :: raw ('RAND ()'))
Alex Naspo


3

Anda dapat dengan mudah menggunakan perintah ini:

// Pertanyaan: nama Model
// ambil 10 baris dari DB Dalam catatan acak ...

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

3

Saya lebih memilih untuk menentukan dulu atau gagal:

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

3

Laravel memiliki metode bawaan untuk mengocok urutan hasil.

Berikut ini kutipan dari dokumentasi:

shuffle()

Metode acak mengacak item dalam koleksi:

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

$shuffled = $collection->shuffle();

$shuffled->all();

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

Anda dapat melihat dokumentasinya di sini .


2

Pada model Anda, tambahkan ini:

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

kemudian pada route / controller

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

2

Ada juga whereRaw('RAND()')yang melakukan hal yang sama, maka Anda dapat rantai ->get()atau ->first()atau bahkan gila dan menambahkan ->paginate(int).


0

Saya punya meja dengan ribuan catatan, jadi saya butuh sesuatu dengan cepat. Ini adalah kode saya untuk baris acak semu:

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

Masalah dengan ini adalah bahwa jika ada beberapa baris dengan id lebih besar dari $counthanya yang pertama yang akan diambil, dan itu juga akan lebih mungkin untuk diambil daripada baris lainnya.
kemika
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.