Obtener una fila aleatoria de una tabla en Rails
Es un patrón bastante común, querer obtener uno (o más) objetos aleatorios de una tabla (modelo en Rails). Ayer mismo lo estuvimos hablando con Jaime en Twitter.
El primer impulso:
item = Model.find(:first, :order => "RAND()")
Y funcionar funciona, pero a nada que la tabla sea un poco grande, estás crujiendo la base de datos, básicamente porque cualquier consulta con ORDER BY que no pueda usar un índice (y obviamente esta no puede) lo hace. Le obligas a ordenar toda la tabla (en La Coctelera hay tablas con más de dos millones de filas) para darte una sola fila.
Este truquito lo recuerdo de mis tiempos PHPeros: miras el número de filas (n), generas un número aleatorio entre 0 y n-1, y usas ese offset para hacer una consulta sin ORDER BY (a.k.a. muy rápida) y con ese OFFSET. En Rails:
n = Model.count o = rand(n - 1) item = Model.find(:first, :limit => 1, :offset => o)
Si quieres más de uno, pues repetir. Muchos tienen que ser para que te salga más caro hacerlo así que con el RAND().
Como es un patrón relativamente común y es un poco feo andar haciendo esas cosas en los controladores, en un ratito que tuve ayer hice un mini-plugin para hacer este tipo de consultas (la verdad es que es tan mínimo que más que para un plugin daba para un pastie y gracias, pero los pasties no se testean y yo sin Caseratests no como
). Y nada, aquí está.





blat dijo
Muy buena, porras. Dani y yo estuvimos peleando el otro día por lo mismo.
En iwanna hicimos algo parecido pero para rellenar un array de n elementos, pero vamos, ya te imaginarás cómo fue.
13 Junio 2008 | 10:58