如何为TSQL选择中的每一行生成随机数?

Mat*_*tin 311 t-sql sql-server sql-server-2000

我需要为表格中的每一行添加一个不同的随机数.以下看似明显的代码对每行使用相同的随机值.

SELECT table_name, RAND() magic_number 
FROM information_schema.tables 
Run Code Online (Sandbox Code Playgroud)

我想从中获得INT或FLOAT.故事的其余部分是我将使用这个随机数来创建一个已知日期的随机日期偏移,例如从开始日期偏移1-14天.

这适用于Microsoft SQL Server 2000.

SQL*_*ace 494

看看SQL Server - 基于集合的随机数,它有一个非常详细的解释.

总而言之,以下代码生成0到13之间的随机数,包括规范化分布:

ABS(CHECKSUM(NewId())) % 14
Run Code Online (Sandbox Code Playgroud)

要更改范围,只需更改表达式末尾的数字即可.如果您需要包含正数和负数的范围,请格外小心.如果你做错了,可以重复数字0.

房间里数学螺母的一个小警告:这段代码有一点点偏差.CHECKSUM()导致在整个sql Int数据类型范围内的数字是统一的,或者至少接近我(编辑器)测试可以显示的数字.但是,当CHECKSUM()在该范围的最高端产生一个数字时,会有一些偏差.每次在最大可能整数和所需范围大小的最后一个精确倍数(在这种情况下为14)之间得到一个数字之前,这个结果优先于你的范围的剩余部分,而不能从这是14的最后一个.

例如,假设Int类型的整个范围仅为19. 19是您可以容纳的最大可能整数.当CHECKSUM()得到14-19时,这些对应于结果0-5.这些数字将大大超过6-13,因为CHECKSUM()生成它们的可能性是其两倍.更直观地展示这一点.下面是我们想象的整数范围的整个可能结果集:

Checksum Integer: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Range Result:     0 1 2 3 4 5 6 7 8 9 10 11 12 13  0  1  2  3  4  5

你可以在这里看到,产生一些数字的机会比其他数字更多:偏见.值得庆幸的是,Int类型的实际范围大得多......以至于在大多数情况下偏差几乎检测不到.但是,如果您发现自己为严肃的安全代码执行此操作,则需要注意这一点.

  • 我们刚刚发现了一个天才的错误.因为校验和返回一个int,并且int的范围是-2 ^ 31(-2,147,483,648)到2 ^ 31-1(2,147,483,647),所以如果结果恰好是-2,147,483,648,则abs()函数可以返回溢出错误!机会显然非常低,大约十亿分之一,但是我们每天在一张~18亿行的桌子上运行它,所以它每周发生一次!修复是在abs之前将校验和强制转换为bigint. (51认同)
  • 这个链接的页面有解决方案:ABS(CHECKSUM(NewId()))%14 (28认同)
  • 我认为这应该说"均匀分布"而不是"归一化分布" - 每个数字都是同样可能的,它不是钟形曲线."归一化"具有特定的数学意义. (13认同)
  • %14将返回0到13之间的数字 (7认同)
  • @Dennis Palmer,只需加1 (7认同)
  • @EvilPuppetMaster,@ MikeHoney你可以先执行'%`操作,然后返回相同的幅度余数,无论是正数还是负数,然后执行`abs`操作:`abs(checksum(newid) ())%14)`.这应该在没有演员的情况下给出相同的结果. (5认同)
  • 只是为了扩展偏置位,如果你在很大的范围内产生数字,那么偏差就会变大.例如,如果您使用的是整数数字空间的2/3,那么在生成的数字空间的下半部分中获取数字的可能性是上半部分的两倍,因此即使是某些临时问题也是如此可能会产生很大的影响.另外,我相信如果你的%数是2的幂,那么应该没有偏见...... (3认同)
  • 我用一个小得多的数据集遇到了 @EvilPuppetMaster 的错误 - 只有 350k 行。触发器在其周围包裹了一个外部选择,并请求随机数列的 AVG()。另外,我没有使用静态 Mod(例如 14),而是使用“COUNT ( * ) OVER ( PARTITION BY 1 )”。无论如何@EvilPuppetMaster 你的解决办法很棒!谢谢并保持邪恶。我的代码现在是: `ABS ( CAST ( CHECKSUM ( NewId() ) AS bigint ) ) % COUNT ( * ) OVER ( PARTITION BY 1 ) + 1 AS [随机行号]` (2认同)

Jer*_*yth 92

在单个批处理中多次调用时,rand()返回相同的数字.

我建议使用convert(varbinary,newid())作为种子参数:

SELECT table_name, 1.0 + floor(14 * RAND(convert(varbinary, newid()))) magic_number 
FROM information_schema.tables
Run Code Online (Sandbox Code Playgroud)

newid() 是保证每个,它被称为时间返回不同的值,即使在同一批次,因此使用它作为种子将提示兰特()每次给予不同的值.

编辑从1到14得到一个随机整数.


Aar*_*man 67

RAND(CHECKSUM(NEWID()))
Run Code Online (Sandbox Code Playgroud)

以上将生成0到1之间的(伪)随机数,不包括.如果在select中使用,因为每行的种子值发生更改,它将为每一行生成一个新的随机数(但不保证每行生成一个唯一的数字).

结合上限10(产生数字1 - 10)的示例:

CAST(RAND(CHECKSUM(NEWID())) * 10 as INT) + 1
Run Code Online (Sandbox Code Playgroud)

Transact-SQL文档:

  1. CAST():https://docs.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql
  2. RAND():http://msdn.microsoft.com/en-us/library/ms177610.aspx
  3. CHECKSUM():http://msdn.microsoft.com/en-us/library/ms189788.aspx
  4. NEWID():https://docs.microsoft.com/en-us/sql/t-sql/functions/newid-transact-sql


Vov*_*ova 38

1000到9999之间的随机数生成:

FLOOR(RAND(CHECKSUM(NEWID()))*(9999-1000+1)+1000)
Run Code Online (Sandbox Code Playgroud)

"+1" - 包括上限值(前一示例为9999)


And*_*nas 19

回答旧问题,但此答案以前没有提供,希望这对通过搜索引擎找到这个结果的人有用.

在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)
Run Code Online (Sandbox Code Playgroud)

或者float在-1和+1之间得到一个,你可以这样做:

select CAST(CRYPT_GEN_RANDOM(8) AS bigint) % 1000000000 / 1000000000.0
Run Code Online (Sandbox Code Playgroud)


Mic*_*Sim 13

如果在表SELECT查询中使用,Rand()函数将生成相同的随机数.如果您将种子用于Rand函数,则同样适用.另一种方法是使用:

SELECT ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS [RandomNumber]
Run Code Online (Sandbox Code Playgroud)

这里获得信息,这很好地解释了问题.


nor*_*ole 5

尝试在 RAND(seedInt) 中使用种子值。RAND() 每个语句只会执行一次,这就是为什么你每次看到相同的数字。


小智 5

如果你不需要它是一个整数,而是任何随机的唯一标识符,你可以使用 newid()

SELECT table_name, newid() magic_number 
FROM information_schema.tables
Run Code Online (Sandbox Code Playgroud)


Cod*_*nis 5

您是否可以将每行中的整数值作为种子传递给RAND函数?

要获得1到14之间的整数,我相信这会起作用:

FLOOR( RAND(<yourseed>) * 14) + 1
Run Code Online (Sandbox Code Playgroud)


Mit*_*lik 5

如果您需要保留种子以便每次都生成"相同"的随机数据,您可以执行以下操作:

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


小智 5

select round(rand(checksum(newid()))*(10)+20,2)
Run Code Online (Sandbox Code Playgroud)

这里的随机数将在 20 到 30 之间。 round最多保留两位小数。

如果你想要负数,你可以用

select round(rand(checksum(newid()))*(10)-60,2)
Run Code Online (Sandbox Code Playgroud)

那么最小值将为 -60,最大值将为 -50。