2011-02-15 34 views

Respuesta

114

En lugar de rand(), use newid(), que se vuelve a calcular para cada fila en el resultado. La forma habitual es usar el módulo de la suma de comprobación. Tenga en cuenta que checksum(newid()) puede producir -2,147,483,648 y causar un desbordamiento de enteros en abs(), por lo que debemos usar módulo en el valor de retorno de suma de comprobación antes de convertirlo a valor absoluto.

UPDATE CattleProds 
SET SheepTherapy = abs(checksum(NewId()) % 10000) 
WHERE SheepTherapy IS NULL 

Esto genera un número aleatorio entre 0 y 9999.

+0

Esta pregunta/respuesta también puede ser útil: http://stackoverflow.com/a/ 9039661/47226 –

+1

Voté como lo sigo usando como referencia. –

+0

Esto no funciona para mí en absoluto. ¿La columna tiene que ser INT? Error # 1064 todo el tiempo. Buscando las píldoras locas ... – freeworlder

19

Si usted está en SQL Server 2008 también puede utilizar

CRYPT_GEN_RANDOM(2) % 10000 

que parece algo más simple (que también se evalúa una vez por fila como newid es - 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 

Devoluciones (2 aleatorio probablemente diferentes números)

col1 
---------------------- 
9693 
8573 

Reflexionando lo inexplicable downvote la única razón legítima que se me ocurre es que debido a que el número aleatorio generado se encuentra entre 0-65535 que no es divisible por 10.000 algunos números será ligeramente más representados . Una forma de evitar esto sería envolverlo en un UDF escalar que elimine cualquier número superior a 60,000 y se llame recursivamente 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 
+1

@downvoter - ¿Alguna razón en particular? Tal vez quiso golpear la flecha hacia arriba ¡esta respuesta funciona bien! –

+0

Lo que parece que falta a todos es que este método es MUCHO MUCHO mejor para el rendimiento. He estado buscando una alternativa a NEWID() y esto es perfecto, ¡gracias! – Digs

6

Mientras que a mi me gusta el uso de suma de comprobación, siento que una mejor manera de ir es el uso de NEWID(), sólo porque usted no tiene que pasar por un complicado matemáticas para generar números simples.

ROUND(1000 *RAND(convert(varbinary, newid())), 0) 

puede reemplazar el 1000 con lo que el número que desea establecer como límite, y siempre se puede utilizar un signo más para crear un rango, digamos que usted quiere un número aleatorio entre 100 y 200, se puede hacer algo como:

100 + ROUND(100 *RAND(convert(varbinary, newid())), 0) 

Juntando todo en su consulta:

UPDATE CattleProds 
SET SheepTherapy= ROUND(1000 *RAND(convert(varbinary, newid())), 0) 
WHERE SheepTherapy IS NULL 
0

probé 2 juegos basados ​​en métodos de aleatorización contra RAND() mediante la generación de 100.000.000 filas con cada uno. Para nivelar el campo, la salida es un flotante entre 0-1 para imitar a RAND(). La mayor parte del código está poniendo a prueba la infraestructura por lo 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 

Usando CRYPT_GEN_RANDOM fue claramente la más aleatoria ya que sólo hay una oportunidad de ver 0,000000001% hasta 1 duplicado cuando el desplume 10^8 números de una conjunto de 10^18 números. IOW 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 

de ejecución de SQL Server tiempos: 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; 

En casi 15 órdenes de magnitud menos aleatorio que este método no era el doble de rápido, teniendo sólo 23 segundos para generar números 100M.

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 

de ejecución de SQL Server tiempos: 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() por sí solo es inútil para la generación basada en conjunto de modo que genera la línea de base para la comparación de aleatoriedad tomó más de 6 horas y tuvo que ser renovadas varias veces para obtener finalmente el número correcto de filas de salida. También parece que la aleatoriedad deja mucho que desear aunque es mejor que usar 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; 
+0

P.S. Pensando que los reinicios podrían haber explicado algunos de los duplicados, probé rápidamente solo filas de 3M, lo que demoró casi 6-1/2 minutos. Obtuve 2101 dups y aparecieron 2 valores 3 veces (.07% y .000067% respectivamente), lo que indica que los reinicios probablemente jugaron un papel, pero la aleatoriedad todavía está lejos de ser estelar. – bielawski

+0

Después de haber notado otra respuesta que acaba de sembrar con newid convertida a varbinary, así lo intenté también. No solo no es más rápido que usar suma de comprobación, sino que un valor aparece 8 veces en esa prueba. Para ser justos, todavía era 95.447319% único, lo que es apenas peor que RAND (Checksum (NewId())) de 95.450254% en mi prueba. Una segunda ejecución arrojó el peor caso de 3 números que aparecen 5 veces y 95.452929% distintos, por lo que YMMV incluso cuando se prueban 100M filas. – bielawski

0
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'"); 
} 
+0

tal vez no es la forma correcta y más fácil pero funciona))) – user3351200

Cuestiones relacionadas