RANDOM() devuelve un valor pseudoaleatorio en coma flotante dentro de [0, 1) y esta detras de casi cualquier consulta "aleatoria": elegir un usuario de muestra, repartir grupos A/B o barajar filas. Veamos como usarlo bien y donde se convierte en un cuello de botella.
El valor base y filas aleatorias
En PostgreSQL, RANDOM() produce cada vez un numero nuevo entre 0 (incluido) y 1 (sin incluir):
SELECT random();
SELECT random() AS r1, random() AS r2;
La forma clasica de sacar filas al azar es ordenar por RANDOM() y tomar la parte superior:
SELECT id, email, country
FROM users
ORDER BY random()
LIMIT 5;
Cada fila recibe una clave de orden aleatoria, asi que el resultado cambia en cada ejecucion. En tablas pequenas se lee con claridad y responde al instante.
Por que ORDER BY RANDOM() es caro
El detalle esta en el coste. Para ordenar por RANDOM(), el motor debe asignar un numero aleatorio a cada fila y ordenar la tabla entera antes de aplicar el LIMIT. Con millones de filas eso es un escaneo completo mas un ordenamiento en disco.
- Un indice sobre la expresion no ayuda: la clave cambia en cada llamada.
- El coste crece con el tamano de la tabla, no con el
LIMIT.
Para tablas grandes usa TABLESAMPLE, que lee paginas al azar en vez de ordenarlo todo:
SELECT id, amount, status
FROM orders TABLESAMPLE BERNOULLI (5);
SELECT * FROM orders TABLESAMPLE SYSTEM (1);
Cuidado: TABLESAMPLE devuelve un porcentaje aproximado, no un numero exacto de filas, y muestrea a nivel de pagina, asi que la distribucion no es perfectamente uniforme. Cuando necesitas exactamente N filas, combina ambos: muestrea de forma gruesa y luego afina.
SELECT id, email
FROM users TABLESAMPLE SYSTEM (10)
ORDER BY random()
LIMIT 5;
Enteros aleatorios
RANDOM() da una fraccion, pero a menudo necesitas un int en un rango. La formula es floor(random() * n) para [0, n):
SELECT floor(random() * 100)::int AS n;
SELECT floor(random() * 6)::int + 1 AS dice;
Un uso practico es repartir empleados en grupos al azar o asignar un descuento aleatorio:
SELECT id, name, dept,
floor(random() * 3)::int AS shard
FROM employees;
Cuidado: no escribas round(random() * n) para un entero uniforme. round tira hacia los extremos la mitad de veces, asi que los bordes (0 y n) reciben la mitad de probabilidad. Usa siempre floor.
Reproducibilidad con setseed
La aleatoriedad incomoda en las pruebas: el resultado cambia en cada ejecucion. setseed() fija el generador para la sesion actual, tomando un valor en [-1, 1]:
SELECT setseed(0.42);
SELECT id FROM users ORDER BY random() LIMIT 3;
Tras setseed, la secuencia de RANDOM() es determinista hasta el siguiente setseed o el fin de la sesion, lo cual es comodo para pruebas y demos repetibles.
MySQL y ClickHouse
La sintaxis difiere entre motores:
- MySQL usa
RAND(), tambien [0, 1). El muestreo es ORDER BY RAND() LIMIT 5; no existe TABLESAMPLE. La semilla va como argumento: RAND(42).
- ClickHouse tiene
rand(), que es un UInt32 en [0, 2^32), no una fraccion. Para [0,1) divide por 4294967295.0; para muestrear ofrece un SELECT ... SAMPLE 0.1 nativo.
SELECT id, email FROM users ORDER BY RAND(42) LIMIT 5;
SELECT id, amount FROM orders SAMPLE 0.1;
La conclusion: ORDER BY RANDOM() es comodo y correcto pero no escala. Con datos grandes pasa al muestreo por paginas y reserva setseed/RAND(seed) para una salida reproducible.
RANDOM()devuelve un valor pseudoaleatorio en coma flotante dentro de[0, 1)y esta detras de casi cualquier consulta "aleatoria": elegir un usuario de muestra, repartir grupos A/B o barajar filas. Veamos como usarlo bien y donde se convierte en un cuello de botella.El valor base y filas aleatorias
En PostgreSQL,
RANDOM()produce cada vez un numero nuevo entre 0 (incluido) y 1 (sin incluir):SELECT random(); -- e.g. 0.8473920192 SELECT random() AS r1, random() AS r2; -- two different valuesLa forma clasica de sacar filas al azar es ordenar por
RANDOM()y tomar la parte superior:SELECT id, email, country FROM users ORDER BY random() LIMIT 5;Cada fila recibe una clave de orden aleatoria, asi que el resultado cambia en cada ejecucion. En tablas pequenas se lee con claridad y responde al instante.
Por que ORDER BY RANDOM() es caro
El detalle esta en el coste. Para ordenar por
RANDOM(), el motor debe asignar un numero aleatorio a cada fila y ordenar la tabla entera antes de aplicar elLIMIT. Con millones de filas eso es un escaneo completo mas un ordenamiento en disco.LIMIT.Para tablas grandes usa
TABLESAMPLE, que lee paginas al azar en vez de ordenarlo todo:-- roughly 5% of physical pages, very cheap SELECT id, amount, status FROM orders TABLESAMPLE BERNOULLI (5); -- block sampling: even faster, coarser SELECT * FROM orders TABLESAMPLE SYSTEM (1);Cuidado:
TABLESAMPLEdevuelve un porcentaje aproximado, no un numero exacto de filas, y muestrea a nivel de pagina, asi que la distribucion no es perfectamente uniforme. Cuando necesitas exactamente N filas, combina ambos: muestrea de forma gruesa y luego afina.SELECT id, email FROM users TABLESAMPLE SYSTEM (10) ORDER BY random() LIMIT 5;Enteros aleatorios
RANDOM()da una fraccion, pero a menudo necesitas uninten un rango. La formula esfloor(random() * n)para[0, n):-- integer in [0, 100) SELECT floor(random() * 100)::int AS n; -- integer in [1, 6] like a dice roll SELECT floor(random() * 6)::int + 1 AS dice;Un uso practico es repartir empleados en grupos al azar o asignar un descuento aleatorio:
SELECT id, name, dept, floor(random() * 3)::int AS shard FROM employees;Cuidado: no escribas
round(random() * n)para un entero uniforme.roundtira hacia los extremos la mitad de veces, asi que los bordes (0 y n) reciben la mitad de probabilidad. Usa siemprefloor.Reproducibilidad con setseed
La aleatoriedad incomoda en las pruebas: el resultado cambia en cada ejecucion.
setseed()fija el generador para la sesion actual, tomando un valor en[-1, 1]:SELECT setseed(0.42); SELECT id FROM users ORDER BY random() LIMIT 3; -- same order on replayTras
setseed, la secuencia deRANDOM()es determinista hasta el siguientesetseedo el fin de la sesion, lo cual es comodo para pruebas y demos repetibles.MySQL y ClickHouse
La sintaxis difiere entre motores:
RAND(), tambien[0, 1). El muestreo esORDER BY RAND() LIMIT 5; no existeTABLESAMPLE. La semilla va como argumento:RAND(42).rand(), que es unUInt32en[0, 2^32), no una fraccion. Para[0,1)divide por4294967295.0; para muestrear ofrece unSELECT ... SAMPLE 0.1nativo.-- MySQL: random rows + reproducible seed SELECT id, email FROM users ORDER BY RAND(42) LIMIT 5; -- ClickHouse: native sampling, no full sort SELECT id, amount FROM orders SAMPLE 0.1;La conclusion:
ORDER BY RANDOM()es comodo y correcto pero no escala. Con datos grandes pasa al muestreo por paginas y reservasetseed/RAND(seed)para una salida reproducible.