Seleccionar fila aleatoria

Seleccionar filas aleatorias de una tabla es útil para mostrar productos destacados en una tienda, elegir ganadores en un sorteo, crear muestras de datos, o generar contenido variado en una aplicación. La solución obvia con ORDER BY RAND() funciona, pero es extremadamente ineficiente en tablas grandes. Veamos las alternativas.

ORDER BY RAND() (forma simple)

La forma más directa de obtener filas aleatorias:

SELECT nombre, precio
FROM productos
ORDER BY RAND()
LIMIT 5;
nombreprecio
Google Pixel 8699.00
Cargador USB-C 65W35.99
MacBook Air M31399.00
Camiseta algodón básica24.99
Mochila senderismo89.99

Cada ejecución devuelve un resultado diferente. El problema es que MySQL genera un número aleatorio para cada fila de la tabla, ordena todas las filas por ese número, y luego descarta todas menos las 5 primeras. En una tabla con 10 millones de filas, esto significa generar 10 millones de números aleatorios y ordenarlos, solo para devolver 5 filas.

Técnica con subconsulta de ID aleatorio

Para tablas con una clave primaria entera sin huecos:

SELECT *
FROM productos
WHERE id = FLOOR(1 + RAND() * (SELECT MAX(id) FROM productos));

Esta consulta genera un ID aleatorio entre 1 y el máximo y busca esa fila directamente por índice. Es extremadamente rápida porque solo necesita una lectura de índice. Sin embargo, si hay huecos en los IDs (por filas eliminadas), puede no devolver resultado. Envuélvela en una estrategia de reintentos o usa la siguiente variante:

SELECT *
FROM productos
WHERE id >= FLOOR(1 + RAND() * (SELECT MAX(id) FROM productos))
ORDER BY id
LIMIT 1;

El operador >= con LIMIT 1 garantiza que siempre devuelve una fila, saltando al siguiente ID existente si el aleatorio no existe.

Múltiples filas aleatorias eficientes

Para obtener varias filas aleatorias sin ORDER BY RAND():

SET @max_id = (SELECT MAX(id) FROM productos);
 
SELECT * FROM productos WHERE id >= FLOOR(1 + RAND() * @max_id) ORDER BY id LIMIT 1
UNION ALL
SELECT * FROM productos WHERE id >= FLOOR(1 + RAND() * @max_id) ORDER BY id LIMIT 1
UNION ALL
SELECT * FROM productos WHERE id >= FLOOR(1 + RAND() * @max_id) ORDER BY id LIMIT 1
UNION ALL
SELECT * FROM productos WHERE id >= FLOOR(1 + RAND() * @max_id) ORDER BY id LIMIT 1
UNION ALL
SELECT * FROM productos WHERE id >= FLOOR(1 + RAND() * @max_id) ORDER BY id LIMIT 1;

Cada subconsulta selecciona una fila aleatoria diferente usando el índice primario. Es posible que ocasionalmente obtengas filas repetidas, pero la probabilidad es baja en tablas grandes.

Técnica con JOIN sobre tabla de IDs

Otra técnica eficiente pre-selecciona IDs aleatorios y luego hace JOIN:

SELECT p.*
FROM productos p
JOIN (
    SELECT FLOOR(1 + RAND() * (SELECT MAX(id) FROM productos)) AS id
    UNION
    SELECT FLOOR(1 + RAND() * (SELECT MAX(id) FROM productos))
    UNION
    SELECT FLOOR(1 + RAND() * (SELECT MAX(id) FROM productos))
    UNION
    SELECT FLOOR(1 + RAND() * (SELECT MAX(id) FROM productos))
    UNION
    SELECT FLOOR(1 + RAND() * (SELECT MAX(id) FROM productos))
) random_ids ON p.id >= random_ids.id
GROUP BY p.id
ORDER BY p.id
LIMIT 5;

ORDER BY RAND() con LIMIT en subconsulta

Si la tabla no es muy grande (menos de 100,000 filas), puedes hacer ORDER BY RAND() más eficiente seleccionando solo los IDs:

SELECT p.*
FROM productos p
JOIN (
    SELECT id FROM productos ORDER BY RAND() LIMIT 5
) AS r ON p.id = r.id;

Al ordenar solo los IDs (una columna de 4 bytes), el ordenamiento es más ligero que ordenar todas las columnas de la tabla.

Fila aleatoria con condiciones

Si necesitas una fila aleatoria que cumpla una condición:

-- Producto aleatorio de electrónica con precio > 100
SELECT *
FROM productos
WHERE categoria_id = 6 AND precio > 100
ORDER BY RAND()
LIMIT 1;

Para tablas grandes con condiciones, la técnica de ID aleatorio es más complicada porque los IDs filtrados pueden tener muchos huecos. Una alternativa es mantener una columna de orden aleatorio que se actualice periódicamente:

ALTER TABLE productos ADD COLUMN random_sort DOUBLE;
UPDATE productos SET random_sort = RAND();
CREATE INDEX idx_random ON productos(random_sort);
 
-- Consulta eficiente con el índice
SELECT * FROM productos
WHERE categoria_id = 6
ORDER BY random_sort
LIMIT 1;

Comparación de rendimiento

Técnica1,000 filas100,000 filas10,000,000 filas
ORDER BY RAND()2 ms150 ms15,000 ms
ID aleatorio (>=)<1 ms<1 ms<1 ms
JOIN sobre IDs random<1 ms<1 ms2 ms
RAND() solo sobre IDs1 ms50 ms5,000 ms

La diferencia de rendimiento se vuelve dramática conforme crece la tabla. Para tablas pequeñas (miles de filas), ORDER BY RAND() es perfectamente aceptable y más simple. Para tablas grandes, las técnicas basadas en IDs aleatorios son esenciales.

La técnica que elijas depende del tamaño de tu tabla y de si puedes asumir la simplicidad de ORDER BY RAND() o necesitas la eficiencia de las alternativas basadas en índices.

Escrito por Eduardo Lázaro