如何在TSQL Select中为每行生成随机数字?
我需要一个不同的随机数在我的表中的每一行。 下面看似明显的代码对每一行使用相同的随机值。
SELECT table_name, RAND() magic_number FROM information_schema.tables
我想要一个INT或一个FLOAT出来。 其余的事情是我将使用随机数字来创建一个从知道日期随机日期偏移量,例如从开始日期偏移1-14天。
这是针对Microsoft SQL Server 2000的。
看看SQL Server – 设置基于随机数字有一个非常详细的解释。
总而言之,下面的代码会生成一个介于0和13之间的随机数,并包含一个规范化的分布:
ABS(CHECKSUM(NewId())) % 14
要改变你的范围,只需改变表达式末尾的数字。 如果您需要包含正数和负数的范围,请特别小心。 如果你做错了,可以重复数字0。
对房间里数学坚果的一个小警告:这个代码有一个很小的偏见。 CHECKSUM()
导致在整个sql Int数据类型范围内规范化的数字,或者至少在我的(编辑器)测试可以显示的范围内。 但是,当CHECKSUM()在该范围的最高端产生一个数字时,会有一些偏差。 任何时候,如果在最大整数之前得到一个最大可能的整数和所需范围的最后一个精确倍数(在这种情况下为14)之间的数字,则这些结果对于范围的其余部分是有利的,那是14的倍数。
举一个例子,想象一下Int类型的整个范围只有19个。19是你可以容纳的最大可能的整数。 当CHECKSUM()结果在14-19时,这些结果对应于结果0-5。 这些数字将会超过6-13,因为CHECKSUM()产生的可能性是其两倍。 视觉上证明这一点更容易。 以下是我们假想的整数范围的所有可能的结果集合:
校验和整数:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 范围结果:0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5
你可以在这里看到,有更多的机会产生一些数字比其他人:偏见。 值得庆幸的是,Int类型的实际范围要大得多,以至于在大多数情况下,偏差几乎检测不到。 但是,如果您发现自己正在执行严重的安全代码,则需要注意这一点。
在单个批次中多次调用时,rand()返回相同的数字。
我建议使用convert( varbinary
, newid()
)作为种子参数:
SELECT table_name, 1.0 + floor(14 * RAND(convert(varbinary, newid()))) magic_number FROM information_schema.tables
newid()
保证在每次调用时都会返回一个不同的值,即使在同一个批处理中,所以使用它作为种子会提示rand()每次给出一个不同的值。
编辑从1到14得到一个随机的整数。
RAND(CHECKSUM(NEWID()))
以上将生成一个0到1之间的(伪)随机数,是唯一的。 如果在select中使用,由于种子值为每一行更改,它会为每一行生成一个新的随机数(但不保证每行生成一个唯一的数字)。
结合上限10(产生数字1 – 10)时的示例:
CAST(RAND(CHECKSUM(NEWID())) * 10 as INT) + 1
Transact-SQL文档:
-
CAST()
: https : //docs.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql -
RAND()
: http : //msdn.microsoft.com/en-us/library/ms177610.aspx -
CHECKSUM()
: http : //msdn.microsoft.com/en-us/library/ms189788.aspx -
NEWID()
: https : //docs.microsoft.com/en-us/sql/t-sql/functions/newid-transact-sql
在1000和9999之间随机生成数字:
FLOOR(RAND(CHECKSUM(NEWID()))*(9999-1000)+1000)
如果在表SELECT查询中使用Rand()函数将生成相同的随机数。 如果使用Rand函数的种子,也是如此。 另一种方法是使用这个:
SELECT ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS [RandomNumber]
从这里得到的信息,这很好地解释了这个问题。
回答旧的问题,但这个答案以前没有提供,希望这将是有用的人通过搜索引擎找到这个结果。
在SQL Server 2008中引入了一个新函数CRYPT_GEN_RANDOM(8)
,它使用CryptoAPI生成一个密码强的随机数,返回值为VARBINARY(8000)
。 这里是文档页面: https : //docs.microsoft.com/en-us/sql/t-sql/functions/crypt-gen-random-transact-sql
所以要得到一个随机数,你可以简单地调用这个函数,并把它转换为必要的类型:
select CAST(CRYPT_GEN_RANDOM(8) AS bigint)
或者获得-1和+1之间的float
,你可以做这样的事情:
select CAST(CRYPT_GEN_RANDOM(8) AS bigint) % 1000000000 / 1000000000.0
你可以在每一行中有一个整数值作为种子传递给RAND函数吗?
要得到一个1到14之间的整数,我相信这是可行的:
FLOOR( RAND(<yourseed>) * 14) + 1
尝试在RAND(seedInt)中使用种子值。 RAND()只会对每个语句执行一次,这就是为什么每次看到相同的数字。
如果你不需要它是一个整数,但任何随机的唯一标识符,你可以使用newid()
SELECT table_name, newid() magic_number FROM information_schema.tables
你需要为每一行调用RAND()。 这是一个很好的例子
如果您需要保留种子,以便每次生成“相同”随机数据,则可以执行以下操作:
1.创建一个返回select rand()的视图
if object_id('cr_sample_randView') is not null begin drop view cr_sample_randView end go create view cr_sample_randView as select rand() as random_number go
2.创建一个UDF,从视图中选择值。
if object_id('cr_sample_fnPerRowRand') is not null begin drop function cr_sample_fnPerRowRand end go create function cr_sample_fnPerRowRand() returns float as begin declare @returnValue float select @returnValue = random_number from cr_sample_randView return @returnValue end go
3.在选择数据之前,给rand()函数种子,然后在select语句中使用UDF。
select rand(200); -- see the rand() function with cte(id) as (select row_number() over(order by object_id) from sys.all_objects) select id, dbo.cr_sample_fnPerRowRand() from cte where id <= 1000 -- limit the results to 1000 random numbers
选择newid()
或者可能这个选择binary_checksum(newid())
我选择的“答案”有时候会遇到的问题是分配不均匀。 如果你需要在很多行中随机分配1到14,你可以这样做(我的数据库有511个表,所以这是有效的,如果你的行数少于随机数跨度,这是行不通的好):
SELECT table_name, ntile(14) over(order by newId()) randomNumber FROM information_schema.tables
这种方式与正常的随机解决方案相反,它保持数字的顺序并随机化另一列。
请记住,我的数据库中有511个表格(这只是我们从information_schema中选择的相关的b / c)。 如果我把前面的查询放到临时表#X中,然后对结果数据运行这个查询:
select randomNumber, count(*) ct from #X group by randomNumber
我得到这个结果,告诉我,我的随机数是非常均匀地分布在许多行中:
select ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) as [Randomizer]
一直为我工作
select round(rand(checksum(newid()))*(10)+20,2)
这里的随机数字会在20到30之间。round将给出两位小数的最大值。
如果你想要负数,你可以这样做
select round(rand(checksum(newid()))*(10)-60,2)
那么最小值将是-60,最大值将是-50。