Laravel - Hàng ngẫu nhiên Eloquent hoặc Fluent


242

Làm cách nào tôi có thể chọn một hàng ngẫu nhiên bằng khung Eloquent hoặc Fluent trong Laravel?

Tôi biết rằng bằng cách sử dụng SQL, bạn có thể đặt hàng bằng RAND (). Tuy nhiên, tôi muốn lấy hàng ngẫu nhiên mà không cần đếm số lượng bản ghi trước truy vấn ban đầu.

Có ý kiến ​​gì không?


Không có cách tốt nhất để làm điều này mà không thực hiện ít nhất hai truy vấn.
NARKOZ

Câu trả lời:


584

Ấu trùng> = 5,2:

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

hoặc là

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

hoặc để có được số lượng hồ sơ cụ thể

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

Ấu trùng 4.2.7 - 5.1:

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

Ấu trùng 4.0 - 4.2.6:

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

Ấu trùng 3:

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

Kiểm tra bài viết này trên các hàng ngẫu nhiên của MySQL. Laravel 5.2 hỗ trợ điều này, đối với phiên bản cũ hơn, không có giải pháp nào tốt hơn sau đó sử dụng RAW Queries .

chỉnh sửa 1: Như Double Gras đã đề cập, orderBy () không cho phép bất cứ điều gì khác sau đó là ASC hoặc DESC kể từ khi thay đổi này . Tôi cập nhật câu trả lời của tôi cho phù hợp.

chỉnh sửa 2: Laravel 5.2 cuối cùng cũng thực hiện chức năng bao bọc cho việc này. Nó được gọi là inRandomOrder () .


81
Thay thế 'nhận' bằng 'đầu tiên' nếu bạn muốn một hàng đơn.
Giá Collin

14
để sử dụng PostgreSQL'RANDOM()'
dwenaus

2
Cảnh báo: trên các bộ dữ liệu lớn, điều này rất chậm, thêm khoảng 900 ms cho tôi
S ..

3
Chúng ta có thể phân trang này?
Irfandi D. Vendy

3
Tuy nhiên, bạn có thể sắp xếp ngẫu nhiên trên mỗi trang mới. Điều này vô nghĩa vì về cơ bản nó giống như bạn nhấn F5.
aebersold

49

Điều này hoạt động tốt,

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

bạn cũng có thể thay đổi đối số trong hàm ngẫu nhiên để có nhiều hơn một bản ghi.

Lưu ý: không được đề xuất nếu bạn có dữ liệu lớn vì điều này sẽ tìm nạp tất cả các hàng trước và sau đó trả về giá trị ngẫu nhiên.


61
Một nhược điểm của hiệu suất là tất cả các hồ sơ được lấy.
Gras đôi

3
ở đây ngẫu nhiên được gọi trên đối tượng bộ sưu tập không phải là truy vấn sql. chức năng ngẫu nhiên được chạy ở phía php
astroanu

@astroanu Phải, nhưng để đưa vào bộ sưu tập đó, tất cả các hàng đều được truy vấn.
MetalFrog

1
Tôi có thể sai, nhưng điều này dường như không hoạt động khi tham số được truyền cho hàm ngẫu nhiên giống với kích thước của bộ sưu tập.
Brynn BHRan

Điều này là không tốt ... Bằng cách này, bạn đang lấy tất cả các bản ghi và nhận một bản ghi ngẫu nhiên. Nếu bảng của bạn có quá nhiều bản ghi thì điều này có thể không tốt cho ứng dụng của bạn.
Anderson Silva

34

tl; dr: Ngày nay, nó đã được triển khai thành Laravel, xem "chỉnh sửa 3" bên dưới.


Đáng buồn thay, như ngày nay có một số cảnh báo với ->orderBy(DB::raw('RAND()'))giải pháp đề xuất:

  • Đó không phải là thuyết bất khả tri. ví dụ: sử dụng SQLite và PostgreSQLRANDOM()
  • Thậm chí tệ hơn, giải pháp này không còn được áp dụng nữa kể từ khi thay đổi này :

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


chỉnh sửa: Bây giờ bạn có thể sử dụng phương thức orderByRaw () : ->orderByRaw('RAND()'). Tuy nhiên đây vẫn không phải là thuyết bất khả tri.

FWIW, CodeIgniter thực hiện một RANDOMhướng sắp xếp đặc biệt , được thay thế bằng ngữ pháp chính xác khi xây dựng truy vấn. Ngoài ra nó có vẻ là khá dễ dàng để thực hiện. Có vẻ như chúng tôi có một ứng cử viên để cải thiện Laravel :)

cập nhật: đây là vấn đề về điều này trên GitHub và yêu cầu kéo chờ xử lý của tôi .


chỉnh sửa 2: Hãy cắt đuổi. Kể từ Laravel 5.1.18, bạn có thể thêm macro vào trình tạo truy vấn:

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

Sử dụng:

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

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


chỉnh sửa 3: Cuối cùng! Kể từ Laravel 5.2.33 ( changelog , PR # 13642 ), bạn có thể sử dụng phương thức gốc inRandomOrder():

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

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

Bạn nên thay đổi tên macro 5.1 thành inRandomOrder để tương thích về phía trước;) chi tiết, chi tiết :)
Sander Visser

Đó chính xác là một điều tôi đã làm trong khi chuẩn bị một dự án 5.1 trước khi chuyển nó sang 5.2.
Gras Double

Đây là một câu trả lời tuyệt vời. Nếu tôi có thể fav một câu trả lời, tôi sẽ!
mwallisch

18

Trong Laravel 4 và 5 , order_bynó được thay thế bằngorderBy

Nên nó phải là:

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

Người dùng :: orderBy (DB :: raw ('RAND ()')) -> get ();
Darius M.

1
Nó hoạt động cảm ơn, nhưng bạn có thể cung cấp một số thông tin làm thế nào điều này hoạt động?
alayli

Bạn có thể cụ thể hơn một chút? Những loại thông tin?
Teodor Talov


9

Đối với Laravel 5.2> =

sử dụng phương pháp Eloquent:

inRandomOrder()

Phương thức inRandomOrder có thể được sử dụng để sắp xếp các kết quả truy vấn một cách ngẫu nhiên. Ví dụ: bạn có thể sử dụng phương pháp này để tìm nạp người dùng ngẫu nhiên:

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

từ tài liệu: https://laravel.com/docs/5.2/queries#ordering-grouping-limit-and-offset


Khóa học :: inRandomOrder () -> mất (20) -> get (); Không hoạt động đối với tôi - đặc tả sắp xếp xấu trong dòng Find.php 219
MJ

1
Điều này hữu ích cho các nhà máy kiểu mẫu hoặc gieo hạt db
Saleh Mahmood

8

Bạn cũng có thể sử dụng phương thức order_by với sự lưu loát và hùng hồn như:

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

Đây là một chút sử dụng kỳ lạ, nhưng hoạt động.

Chỉnh sửa: Như @Alex đã nói, cách sử dụng này sạch hơn và cũng hoạt động:

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

3
cái này cũng hoạt động tốt và sạch hơn một chút .. -> order_by (\ DB :: raw ('RAND ()'))
Alex Naspo


3

Bạn có thể dễ dàng sử dụng lệnh này:

// Câu hỏi: tên của Model
// lấy 10 hàng từ DB Trong các bản ghi xáo trộn ...

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

3

Tôi thích chỉ định đầu tiên hoặc thất bại:

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

3

Laravel có một phương pháp tích hợp để xáo trộn thứ tự kết quả.

Đây là một trích dẫn từ các tài liệu:

shuffle()

Phương pháp xáo trộn ngẫu nhiên xáo trộn các mục trong bộ sưu tập:

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

$shuffled = $collection->shuffle();

$shuffled->all();

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

Bạn có thể xem tài liệu ở đây .


2

Tại mô hình của bạn thêm điều này:

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

sau đó tại tuyến đường / bộ điều khiển

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

2

Ngoài ra còn có whereRaw('RAND()')mà cũng làm như vậy, bạn có thể sau đó chuỗi ->get()hoặc ->first()hoặc thậm chí phát điên và thêm ->paginate(int).


0

Tôi có bảng với hàng ngàn hồ sơ, vì vậy tôi cần một cái gì đó nhanh chóng. Đây là mã của tôi cho hàng ngẫu nhiên giả:

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

Vấn đề với điều này là nếu có nhiều hàng có id lớn hơn $countchỉ hàng đầu tiên trong số này sẽ được truy xuất, và do đó, nó cũng sẽ có khả năng được truy xuất hơn bất kỳ hàng nào khác.
kemika
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.