Es cierto que SQL Server tiene la función RAND(), la cual genera un número aleatorio, pero este no funciona correctamente o por lo menos como uno lo espera, por lo tanto aquí vamos a explicar como generar los números aleatorios de forma correcta.
Por ejemplo RAND() si es ejecutado en transacciones distintas funciona perfectamente pero si es ejecutado varias veces en una misma transacción siempre nos va a retornar el mismo valor, por lo tanto el random no funciona.
Para probar esto que acabo de decir vamos a abrir el Management Studio y en un query nuevo hacemos
SELECT newid() AS 'New id', RAND() AS 'Random', CHECKSUM(newid()) AS 'Checksum'
Resultado
New Id | Random | Checksum | |
1 | F3201996-A5E7-4B44-BDDE-FFF2A2499AB1 | 0.542140206197715 | 133238726 |
Resultado siguiente ves que se ejecuta
New Id | Random | Checksum | |
1 | 41B78E4A-9F73-47E0-8721-2190E699E696 | 0.216229945261277 | 926182060 |
Hasta aquí todo bien, las dos veces se ejecutó correctamente el random, como debe de ser, pero si se ejecuta varias veces en la misma transacción el resultado es otro
SELECT TOP 5 newid() AS 'New id', RAND() AS 'Mal Random', CHECKSUM(newid()) AS 'Checksum' FROM sys.tables
Resultado
New Id | Mal Random | Checksum | |
1 | C797611C-356C-47E4-97AD-14B331CECD7D | 0.029692678590302 | 1529445979 |
2 | 12600DA6-B399-4F31-9BED-186EECFAA896 | 0.029692678590302 | -1163175142 |
3 | 7691D246-73DE-4DD6-944B-0FA3523F0890 | 0.029692678590302 | 1893578383 |
4 | 69D663F5-0F40-4732-97FE-CB7F85B22E39 | 0.029692678590302 | 526397255 |
5 | F82D3E07-66BF-4579-95D6-C745D9FAAF6E | 0.029692678590302 | -1752607711 |
Como podemos ver el valor Random siempre es el mismo, esto se debe a que si a la función Random el seed que utiliza SQL para generar el random es siempre el mismo, el seed de la transacción, por lo tanto el valor retorno siempre es el mismo. Entonces cómo se puede generar un valor random correcto?
En los query de ejemplo podemos ver la columna Checksum, la cual nos está retornando valores diferente en cada resultado, ya sea en transacciones distintas o en la misma transacción. Aquí está la clave para nuestro random.
Solución
New Id es un valor que genera SQL tomando como base el MAC address de la tarjeta de red y el reloj de la computadora, esto nos genera un string muy largo pero siempre distinto. Si a este valor le aplicamos la función checksum [ checksum(newid()) ] nos genera un valor numérico entero, que en algunos casos puede ser negativo. Si ocupamos que nuestro valor random sea un numérico entero positivo simplemente le aplicamos la funcion absolute [ abs(checksum(newid())) ]
Entonces si ahora combinamos estas tres funciones de SQL obtendremos el random que necesitamos
SELECT TOP 5 RAND() AS 'Mal Random', abs(checksum(newid())) AS 'RANDOM' FROM sys.tables
Resultado
Mal Random | RANDOM | |
1 | 0.747613544929705 | 149303544 |
2 | 0.747613544929705 | 2063275895 |
3 | 0.747613544929705 | 1150964410 |
4 | 0.747613544929705 | 1372336738 |
5 | 0.747613544929705 | 2115100305 |
Como podemos ver en los resultados, en cada ejecución la columna Random retorna de forma correcta los valores aleatorios siempre diferentes, sin importar la sesión en la que estemos ejecutando la consulta.
Hola, y si quiero acotar ese número aleatorio, ponerle un rango, ¿?
a que te refieres con acotar?
a esta forma de generar un Random no se le puede poner un rango, precisamente ese es el objetivo de la función.
Muy interesante el artículo. Pero la característica principal de un generador de números aleatorios es que genere números entre 0 y 1 excluyendo ambos.
En el ejemplo podrías convertir los numeros así:
149303544 sería 0.149303544
2063275895 sería 0.2063275895
1150964410 sería 0.1150964410 etc.
Si deseas ese resultado, fácilmente puedes modificar el código para obtener el número en el rango deseado.
Excelente artículo. Resulto ser de utilidad para mí. Me gusta, ademas del contenido, el aspecto estético de tu sitio. Saludos.
Excelente, muchas gracias por el comentario
Hola, consulta, ¿aplicando el ABS significa que alguna vez podría obtener el mismo número pero 1 que fue negativo y el otro que fue positivo (antes del ABS)? Esto porque quería generar un identificador aleatorio para una tabla que crece constantemente y ya va en cerca de 1 millón de registros. El tema es que por auditoria no nos permiten tener las claves originales para QA y debemos reemplazarlas por otras al azar, gracias
En realidad no se van a repetir, el newid te va a generar un valor unico por equipo. El new id te va a retornar un valor como ‘A972C577-DFB0-064E-1189-0154C99310DAAC12’ a ese lo transformamos en integer con checksum y que es un valor numérico que representa el string generado.
No se van a repetir datos, igual puedes dejar el calculo sin en abs si gustas, pero un valor random deberia de ser entero positivo, y en buenas practicas debe de estar entre 0 y 1 pero este ejemplo funciona bien.
[…] un artículo anterior se había publicado Como generar correctamente un numero random en SQL Server. En este ejemplo vamos a explicar como retornar datos random de un select, retornar filas […]