UPDATE CattleProds
SET SheepTherapy=(ROUND((RAND()* 10000),0))
WHERE SheepTherapy IS NULL
Si luego hago un SELECCIONAR, veo que mi número aleatorio es idéntico en cada fila . ¿Alguna idea de cómo generar números aleatorios únicos?
UPDATE CattleProds
SET SheepTherapy=(ROUND((RAND()* 10000),0))
WHERE SheepTherapy IS NULL
Si luego hago un SELECCIONAR, veo que mi número aleatorio es idéntico en cada fila . ¿Alguna idea de cómo generar números aleatorios únicos?
Respuestas:
En lugar de rand()
, use newid()
, que se vuelve a calcular para cada fila del resultado. La forma habitual es utilizar el módulo de la suma de comprobación. Tenga en cuenta que checksum(newid())
puede producir -2,147,483,648 y provocar un desbordamiento de enteros abs()
, por lo que debemos usar módulo en el valor de retorno de la suma de comprobación antes de convertirlo en valor absoluto.
UPDATE CattleProds
SET SheepTherapy = abs(checksum(NewId()) % 10000)
WHERE SheepTherapy IS NULL
Esto genera un número aleatorio entre 0 y 9999.
Si está en SQL Server 2008, también puede usar
CRYPT_GEN_RANDOM(2) % 10000
Lo que parece algo más simple (también se evalúa una vez por fila como newid
se muestra a continuación)
DECLARE @foo TABLE (col1 FLOAT)
INSERT INTO @foo SELECT 1 UNION SELECT 2
UPDATE @foo
SET col1 = CRYPT_GEN_RANDOM(2) % 10000
SELECT * FROM @foo
Devuelve (2 números aleatorios probablemente diferentes )
col1
----------------------
9693
8573
Reflexionando sobre el voto negativo inexplicable, la única razón legítima que puedo pensar es que debido a que el número aleatorio generado está entre 0-65535, que no es divisible de manera uniforme por 10,000, algunos números estarán ligeramente sobre representados. Una forma de evitar esto sería envolverlo en una UDF escalar que deseche cualquier número superior a 60.000 y se llame a sí mismo de forma recursiva para obtener un número de reemplazo.
CREATE FUNCTION dbo.RandomNumber()
RETURNS INT
AS
BEGIN
DECLARE @Result INT
SET @Result = CRYPT_GEN_RANDOM(2)
RETURN CASE
WHEN @Result < 60000
OR @@NESTLEVEL = 32 THEN @Result % 10000
ELSE dbo.RandomNumber()
END
END
Si bien me encanta usar CHECKSUM, creo que una mejor manera de hacerlo es usar NEWID()
, solo porque no tiene que pasar por una matemática complicada para generar números simples.
ROUND( 1000 *RAND(convert(varbinary, newid())), 0)
Puede reemplazar el 1000
con el número que desee establecer como límite, y siempre puede usar un signo más para crear un rango, digamos que desea un número aleatorio entre 100
y 200
, puede hacer algo como:
100 + ROUND( 100 *RAND(convert(varbinary, newid())), 0)
Poniéndolo junto en su consulta:
UPDATE CattleProds
SET SheepTherapy= ROUND( 1000 *RAND(convert(varbinary, newid())), 0)
WHERE SheepTherapy IS NULL
Probé 2 métodos de aleatorización basados en conjuntos contra RAND () generando 100,000,000 filas con cada uno. Para nivelar el campo, la salida es un valor flotante entre 0-1 para imitar RAND (). La mayor parte del código está probando la infraestructura, así que resumo los algoritmos aquí:
-- Try #1 used
(CAST(CRYPT_GEN_RANDOM(8) AS BIGINT)%500000000000000000+500000000000000000.0)/1000000000000000000 AS Val
-- Try #2 used
RAND(Checksum(NewId()))
-- and to have a baseline to compare output with I used
RAND() -- this required executing 100000000 separate insert statements
El uso de CRYPT_GEN_RANDOM fue claramente el más aleatorio, ya que solo hay un .000000001% de probabilidad de ver incluso 1 duplicado al extraer 10 ^ 8 números DE un conjunto de 10 ^ 18 números. ¡OIA no deberíamos haber visto ningún duplicado y este no tenía ninguno! Este conjunto tardó 44 segundos en generarse en mi computadora portátil.
Cnt Pct
----- ----
1 100.000000 --No duplicates
Tiempos de ejecución de SQL Server: tiempo de CPU = 134795 ms, tiempo transcurrido = 39274 ms.
IF OBJECT_ID('tempdb..#T0') IS NOT NULL DROP TABLE #T0;
GO
WITH L0 AS (SELECT c FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS D(c)) -- 2^4
,L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B) -- 2^8
,L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B) -- 2^16
,L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B) -- 2^32
SELECT TOP 100000000 (CAST(CRYPT_GEN_RANDOM(8) AS BIGINT)%500000000000000000+500000000000000000.0)/1000000000000000000 AS Val
INTO #T0
FROM L3;
WITH x AS (
SELECT Val,COUNT(*) Cnt
FROM #T0
GROUP BY Val
)
SELECT x.Cnt,COUNT(*)/(SELECT COUNT(*)/100 FROM #T0) Pct
FROM X
GROUP BY x.Cnt;
Con casi 15 órdenes de magnitud menos aleatorios, este método no fue el doble de rápido, y solo tomó 23 segundos para generar 100 millones de números.
Cnt Pct
---- ----
1 95.450254 -- only 95% unique is absolutely horrible
2 02.222167 -- If this line were the only problem I'd say DON'T USE THIS!
3 00.034582
4 00.000409 -- 409 numbers appeared 4 times
5 00.000006 -- 6 numbers actually appeared 5 times
Tiempos de ejecución de SQL Server: tiempo de CPU = 77156 ms, tiempo transcurrido = 24613 ms.
IF OBJECT_ID('tempdb..#T1') IS NOT NULL DROP TABLE #T1;
GO
WITH L0 AS (SELECT c FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) AS D(c)) -- 2^4
,L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B) -- 2^8
,L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B) -- 2^16
,L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B) -- 2^32
SELECT TOP 100000000 RAND(Checksum(NewId())) AS Val
INTO #T1
FROM L3;
WITH x AS (
SELECT Val,COUNT(*) Cnt
FROM #T1
GROUP BY Val
)
SELECT x.Cnt,COUNT(*)*1.0/(SELECT COUNT(*)/100 FROM #T1) Pct
FROM X
GROUP BY x.Cnt;
RAND () solo es inútil para la generación basada en conjuntos, por lo que generar la línea de base para comparar la aleatoriedad tomó más de 6 horas y tuvo que reiniciarse varias veces para finalmente obtener el número correcto de filas de salida. También parece que la aleatoriedad deja mucho que desear, aunque es mejor que usar la suma de comprobación (newid ()) para resembrar cada fila.
Cnt Pct
---- ----
1 99.768020
2 00.115840
3 00.000100 -- at least there were comparitively few values returned 3 times
Debido a los reinicios, no se pudo capturar el tiempo de ejecución.
IF OBJECT_ID('tempdb..#T2') IS NOT NULL DROP TABLE #T2;
GO
CREATE TABLE #T2 (Val FLOAT);
GO
SET NOCOUNT ON;
GO
INSERT INTO #T2(Val) VALUES(RAND());
GO 100000000
WITH x AS (
SELECT Val,COUNT(*) Cnt
FROM #T2
GROUP BY Val
)
SELECT x.Cnt,COUNT(*)*1.0/(SELECT COUNT(*)/100 FROM #T2) Pct
FROM X
GROUP BY x.Cnt;
require_once('db/connect.php');
//rand(1000000 , 9999999);
$products_query = "SELECT id FROM products";
$products_result = mysqli_query($conn, $products_query);
$products_row = mysqli_fetch_array($products_result);
$ids_array = [];
do
{
array_push($ids_array, $products_row['id']);
}
while($products_row = mysqli_fetch_array($products_result));
/*
echo '<pre>';
print_r($ids_array);
echo '</pre>';
*/
$row_counter = count($ids_array);
for ($i=0; $i < $row_counter; $i++)
{
$current_row = $ids_array[$i];
$rand = rand(1000000 , 9999999);
mysqli_query($conn , "UPDATE products SET code='$rand' WHERE id='$current_row'");
}