如何在SQL中用随机数填充列?每行我得到相同的值


84
UPDATE CattleProds
SET SheepTherapy=(ROUND((RAND()* 10000),0))
WHERE SheepTherapy IS NULL

如果再执行SELECT,我会发现我的随机数在每一行中都是相同的。有什么想法如何生成唯一的随机数吗?

Answers:


166

代替rand(),使用newid(),对结果中的每一行都重新计算。通常的方法是使用校验和的模。请注意,这checksum(newid())可能产生-2,147,483,648并导致上的整数溢出abs(),因此我们需要在对校验和返回值进行模运算之前将其转换为绝对值。

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

这将生成一个介于0和9999之间的随机数。


1
这个问题/答案可能也有帮助:stackoverflow.com/a/9039661/47226
Aaron Hoffman 2013年

这根本不适合我。列是否必须为INT?每次错误#1064。到达疯狂的药丸……
Freeworlder 2014年

1
这是一件美丽的事!做得好。爱它。性能略有降低,但仍然不错。
阿文·阿米尔

25

如果您使用的是SQL Server 2008,也可以使用

 CRYPT_GEN_RANDOM(2) % 10000

这似乎更简单(它也按行计算一次,newid如下所示)

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

返回值(2个随机的可能不同的数字)

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

考虑无法解释的下降投票,我能想到的唯一合理原因是,因为生成的随机数在0-65535之间,不能被10,000整除,所以有些数字会略有超出。一种解决方法是将其包装在标量UDF中,该标量UDF会丢弃超过60,000的任何数字,然后递归调用自身以获取替换数字。

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-任何特殊原因?也许您是想按向上箭头键,所以此答案很好用!
马丁·史密斯

每个人似乎都缺少的是,这种方法在性能上要好得多。我一直在寻找NEWID()的替代方法,这很不错,谢谢!
Digs 2013年

任何期望的范围都可以轻松应对。例如,ABS(CAST(CRYPT_GEN_RANDOM(8)AS BIGINT)%10001)产生一个0到10000之间的数字,这是OP的代码按照其希望的方式工作时将生成的范围。
bielawski

哪个“相同”的问题?该公式的确会为每行生成新值(解决了op的问题),结果在范围内,但不会出现偏斜,因为有64位种子,只有14位结果,因此任何潜在的偏斜均不可检测。即使您生成了10 ^ 15的结果,您可能会认为您检测到的任何偏斜仍在误差范围内。这意味着您需要生成2 ^ 19个结果来证明实际存在偏斜。
bielawski

9

虽然我确实喜欢使用CHECKSUM,但我觉得使用它是一种更好的方法NEWID(),因为您不必经过复杂的数学运算即可生成简单的数字。

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

您可以将1000您要设置为限制的数字替换为,并且始终可以使用加号来创建范围,例如,您想要在100和之间的随机数200,可以执行以下操作:

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

将其放到您的查询中:

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

1

我针对RAND()测试了2种基于集合的随机方法,每个方法都产生了100,000,000行。为了平整该字段,输出是一个介于0-1之间的浮点,以模拟RAND()。大多数代码正在测试基础结构,因此我在这里总结算法:

-- 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

显然,使用CRYPT_GEN_RANDOM是最随机的,因为从一组10 ^ 18的数字中抽出10 ^ 8的数字时,甚至只有.000000001%的机会看到甚至1个重复项。哎呀,我们不应该看到任何重复的,而且没有!在我的笔记本电脑上生成此设置花了44秒钟。

Cnt     Pct
-----   ----
 1      100.000000  --No duplicates

SQL Server执行时间:CPU时间= 134795毫秒,经过的时间= 39274毫秒。

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;

这种方法的随机性降低了近15个数量级,其速度并不快两倍,仅需23秒即可生成1亿个数字。

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 

SQL Server执行时间:CPU时间= 77156 ms,经过的时间= 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()本身对于基于集合的生成毫无用处,因此生成用于比较随机性的基准要花费6个小时以上的时间,必须重新启动几次才能最终获得正确数量的输出行。看起来随机性还有很多需要改进的地方,尽管它比使用checksum(newid())重新设定每行的种子更好。

Cnt  Pct
---- ----
1    99.768020
2    00.115840
3    00.000100  -- at least there were comparitively few values returned 3 times

由于重新启动,无法捕获执行时间。

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;

PS认为重新启动可能是造成某些重复的原因,所以我很快就测试了3M行,耗时近6-1 / 2分钟。我得到2101次重复,出现2次值3次(分别为.07%和.000067%),这表明重新启动可能起了一定作用,但随机性仍远非恒星。
bielawski

注意到刚刚将newid转换为varbinary的另一个答案后,我也尝试了。它不仅比使用校验和更快,而且在该测试中一个值出现8次。公平地说,它仍然具有95.447319%的唯一性,这仅比我测试中RAND(Checksum(NewId()))的95.450254%差一点。第二次执行产生3个数字的最坏情况,出现5次,相差95.452929%,因此即使在测试100M行时,YMMV也是如此。
bielawski

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

也许它不正确,最简单,但是有效)))
Vaso Nadiradze 17/12/13

1
在开始回答之前,请仔细阅读问题。顺便说一句,分别发送每行的UPDATE查询是一种非常非常不好的想法,即使必须更新很少的行数也是如此。
darlove
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.