SqlServer Rand()问题

时间:2009-11-01 15:47:42

标签: sql-server random

我正在编写一个程序,每次调用都需要一个随机数。从我们的.net Web服务调用此过程。

我尝试使用rand()实现此功能。但是,当我在几毫秒内对存储过程进行多次调用时,我发生了很多冲突,因为生成了相同的随机数。如果在后续呼叫之间有大约20或30毫秒的空间,它似乎可以正常工作。

SqlServer似乎重新调用了每个存储过程调用的rand()。从我的理解这是一个问题,因为一个应该播种一个随机数生成器,如果一个重播每次调用rand,那个没有得到一个好的伪随机数序列。此外,似乎在1或2毫秒内调用相同的sp会获得相同的值。

这是存储过程中的语句本身。

DECLARE @randomNumber char(9)

SET @randomNumber = RIGHT('00000' + CAST(CAST(rand()*100000 AS INT) AS VARCHAR(5)),5)
+ RIGHT('00000' + CAST(CAST(rand()*10000 AS INT) AS VARCHAR(4)),4)

有没有人有解决这个问题的建议?

我是否必须编写自己的一次播种的随机数生成器并将其状态保存在表中? SQL Server种子rand()如何?它是真正随机的,或者如果你在一个连接上在一个或两个毫秒内调用一个sp,它是否会用同一个种子播种导致冲突?

4 个答案:

答案 0 :(得分:9)

如果您使用的是SQL Server 2008,则可以使用CRYPT_GEN_RANDOM()函数。这将使每行的数据随机化,即使您在一次查询执行中尝试计算数百万个随机数并且没有任何种子问题:

SELECT CAST(RIGHT(CAST(CAST(CRYPT_GEN_RANDOM(1) AS INT) AS VARCHAR(100)), 1) AS INT)

以下是BOL文章的链接:

http://msdn.microsoft.com/en-us/library/cc627408.aspx

答案 1 :(得分:5)

在您的示例中,将rand()*10000替换为ABS(CHECKSUM(NEWID())) % 9999

然而,对于char(9):

SELECT RIGHT('000000000' + CAST(ABS(CHECKSUM(NEWID()) % 999999999) AS char(9), 9)

随机种子RAND ...

RAND(CHECKSUM(NEWID()))

编辑:

注意,RAND在SQL Server中实现得很糟糕。不要使用它。

答案 2 :(得分:0)

RAND() function有一个可选的种子参数,您可以将其用于此目的。如果将最后生成的随机值作为种子传递给下一次调用rand(),则可以保证获得一个新的随机数。

感谢gbn指出种子是一个整数,而rand()返回一个浮点数。有了这些知识,这是一个有效的例子!首先创建一个表:

create table RandomNumber (number float)
insert into RandomNumber values (rand())

然后获取一个随机数并将新号码存储在一个交易中:

declare @new float
begin transaction
select @new = rand(-2147483648 + 4294967295 * number)
    from RandomNumber with (updlock, holdlock)
update RandomNumber set number = @new
commit transaction
print 'Next bingo number is: ' + cast(cast(@new*100 as int) as varchar)

SQL Server整数在-2147483648和2147483647之间变化,随机数是介于0.0和1.0之间的浮点数。所以-2147483648 + 4294967295 * number应涵盖所有可用整数。

事务确保一次只有一个连接读取并存储新号码。因此,即使在与SQL Server的不同连接上,这些数字也是随机的。 (顺便说一句,我投票赞成gbn的答案,似乎更容易。)

答案 3 :(得分:0)

您可以使用仅包含标识符字段的表来创建用作种子的唯一数字:

declare
  @randomNumber char(9),
  @seed1 int,
  @seed2 int

insert into SeedTable () values ()
set @seed1 = scope_identity()

insert into SeedTable () values ()
set @seed2 = scope_identity()

set @randomNumber = right('00000' + 
    cast(cast(rand(@seed1) * 100000 as int) as varchar(5)), 5) +
    right('00000' + 
    cast(cast(rand(@seed2) * 10000 as int) as varchar(4)), 4)

if (@seed2 > 10000) truncate table SeedTable