Laravel: fila aleatoria elocuente o fluida

242

¿Cómo puedo seleccionar una fila aleatoria usando Eloquent o Fluent en el marco de Laravel?

Sé que al usar SQL, puede ordenar por RAND (). Sin embargo, me gustaría obtener la fila aleatoria sin contar el número de registros antes de la consulta inicial.

¿Algunas ideas?

DigitalWM
fuente
No hay mejor manera de hacerlo sin ejecutar al menos dos consultas.
NARKOZ

Respuestas:

584

Laravel> = 5.2:

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

o

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

o para obtener el número específico de registros

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

Consulte este artículo sobre filas aleatorias de MySQL. Laravel 5.2 es compatible con esto, para una versión anterior, no hay mejor solución que usar consultas RAW .

editar 1: como mencionó Double Gras, orderBy () no permite nada más que ASC o DESC desde este cambio. Actualicé mi respuesta en consecuencia.

edit 2: Laravel 5.2 finalmente implementa una función de contenedor para esto. Se llama inRandomOrder () .

Aebersold
fuente
81
Reemplace 'get' con 'first' si desea una sola fila.
Precio de Collin
14
para uso PostgreSQL'RANDOM()'
dwenaus
2
Advertencia: en grandes conjuntos de datos esto es muy lento, agregando alrededor de 900 ms para mí
S
3
¿Podemos paginar esto?
Irfandi D. Vendy
3
Puede, sin embargo, la clasificación será aleatoria en cada página nueva. Lo que no tiene sentido porque es esencialmente lo mismo que presionas F5.
aebersold
49

Esto funciona bien

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

También puede cambiar el argumento en función aleatoria para obtener más de un registro.

Nota: no se recomienda si tiene datos enormes, ya que esto buscará primero todas las filas y luego devolverá un valor aleatorio.

viril
fuente
61
Una desventaja en cuanto al rendimiento es que se recuperan todos los registros.
Gras Double
3
aquí se llama al azar en el objeto de colección, no en la consulta sql. la función aleatoria se ejecuta en el lado php
astroanu
@astroanu Correcto, pero para completar esa colección, se consultan todas las filas.
MetalFrog
1
Podría estar equivocado, pero esto no parece funcionar cuando el parámetro pasado a la función aleatoria es el mismo que el tamaño de la colección.
Brynn Bateman el
Esto no es bueno ... De esta manera está recuperando todos los registros y obteniendo uno aleatorio. Si su tabla tiene demasiados registros, esto podría ser malo para su aplicación.
Anderson Silva
34

tl; dr: Actualmente está implementado en Laravel, ver "editar 3" a continuación.


Lamentablemente, a partir de hoy hay algunas advertencias con la ->orderBy(DB::raw('RAND()'))solución propuesta:

  • No es independiente de DB. por ejemplo, uso de SQLite y PostgreSQLRANDOM()
  • Peor aún, esta solución ya no es aplicable ya que este cambio :

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


editar: ahora puede usar el método orderByRaw () :->orderByRaw('RAND()') . Sin embargo, esto todavía no es independiente de DB.

FWIW, CodeIgniter implementa un especial RANDOM dirección de clasificación , que se reemplaza con la gramática correcta al generar la consulta. También parece ser bastante fácil de implementar. Parece que tenemos un candidato para mejorar Laravel :)

actualización: aquí está el problema sobre esto en GitHub, y mi solicitud de extracción pendiente .


editar 2: cortemos la persecución. Desde Laravel 5.1.18 puede agregar macros al generador de consultas:

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

Uso:

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

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


Edición 3: ¡Finalmente! Desde Laravel 5.2.33 ( registro de cambios , PR # 13642 ) puede usar el método nativo inRandomOrder():

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

DB::table('users')->where('active', 1)->inRandomOrder()->limit(10)->get();
Gras Double
fuente
Debe cambiar el nombre de la macro 5.1 a inRandomOrder para que sea compatible con versiones anteriores;) detalles, detalles :)
Sander Visser
Eso es precisamente una cosa que hice mientras preparaba un proyecto 5.1 antes de migrarlo a 5.2.
Gras Double
Esta es una gran respuesta. Si pudiera favorecer una respuesta, ¡lo haría!
mwallisch
18

En laravel 4 y 5 el order_byse sustituye pororderBy

Entonces, debería ser:

User::orderBy(DB::raw('RAND()'))->get();
Teodor Talov
fuente
Usuario :: orderBy (DB :: raw ('RAND ()')) -> get ();
Darius M.
1
Funciona gracias, pero ¿podría dar alguna información sobre cómo funciona?
alayli
¿Podrías ser un poco más específico? ¿Que tipo de informacion?
Teodor Talov
17

Puedes usar :

ModelName::inRandomOrder()->first();
simhumileco
fuente
9

Para Laravel 5.2> =

usa el método Eloquent:

inRandomOrder()

El método inRandomOrder puede usarse para ordenar los resultados de la consulta al azar. Por ejemplo, puede usar este método para buscar un usuario aleatorio:

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

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

Manuel Azar
fuente
Curso :: inRandomOrder () -> take (20) -> get (); No funciona para mí: especificación de clasificación incorrecta en Find.php línea 219
MJ
1
Este es útil para fábricas modelo o siembra de db
Saleh Mahmood
8

También puede usar el método order_by con fluidez y elocuencia como:

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

Este es un uso un poco extraño, pero funciona.

Editar: como dijo @Alex, este uso es más limpio y también funciona:

Posts::where_status(1)->order_by(DB::raw('RAND()'));
Bilal Gultekin
fuente
3
esto también funciona y es un poco más limpio .. -> order_by (\ DB :: raw ('RAND ()'))
Alex Naspo
3

Usar la función Laravel

ModelName::inRandomOrder()->first();
Kamlesh Paul
fuente
3

Puede usar fácilmente este comando:

// Pregunta: nombre del modelo
// tomar 10 filas de la base de datos en registros aleatorios ...

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

Prefiero especificar primero o fallar:

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

Laravel tiene un método incorporado para barajar el orden de los resultados.

Aquí hay una cita de la documentación:

shuffle()

El método aleatorio baraja aleatoriamente los elementos de la colección:

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

$shuffled = $collection->shuffle();

$shuffled->all();

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

Puedes ver la documentación aquí .

AlmostPitt
fuente
2

En su modelo agregue esto:

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

luego en ruta / controlador

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

También hay whereRaw('RAND()')que hace lo mismo, a continuación, puede cadena ->get()o ->first()incluso volverse loco y añadir ->paginate(int).

ctf0
fuente
0

Tengo una tabla con miles de registros, por lo que necesito algo rápido. Este es mi código para la fila pseudoaleatoria:

// 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
fuente
El problema con esto es que si hay varias filas con identificadores mayores que $countsolo se recuperaría el primero de ellos, por lo que también sería más probable que se recupere que cualquier otra fila.
kemika