CHECKSUM(NewId())每行执行多次

时间:2012-02-27 11:06:46

标签: sql sql-server random newid

在查看代码时,我遇到了一些奇怪的事情,有人读过你可以使用ABS(CHECKSUM(NewId())) % N来获取从0到N-1的随机数(因为每行不会出现RAND()) ,但我怀疑他们没有真正测试他们的代码(现在用表变量简化,实际代码执行TOP 1 country以错误地解决下面的问题):

DECLARE @Values TABLE
(
    id int identity,
    country VARCHAR(100)
)

INSERT INTO @Values (country) VALUES ('UK'), ('USA'), ('China')

SELECT *, (SELECT country FROM @Values v WHERE v.id = ABS(CHECKSUM(NewId())) % 3 + 1)
FROM @Values

执行时会发生以下错误:

  

子查询返回的值超过1。当子查询遵循=,!=,<,< =,>,> =或子查询用作表达式时,不允许这样做。

我的第一个问题是子查询如何返回多个值?如何在某些时候根本不返回任何价值呢? (注意不是我要问的问题在SO)测试:

SELECT country FROM @Values v WHERE v.id = ABS(CHECKSUM(NewId())) % 3 + 1

然而,无论执行以下操作多少次:

SELECT ABS(CHECKSUM(NewId())) % 3 + 1

返回的结果总是1,2,3(这是程序员在编写代码时使用的'测试')

从这一切开始,我怀疑这是因为它被重新执行每次比较而不是每一行,有人可以确认这一点,并提供一个很好的链接来解释这种行为,以便我可以指出它吗?

2 个答案:

答案 0 :(得分:2)

为什么,这是预期的。

ABS(CHECKSUM(NewId())) % 3 + 1中的每一行评估

@Values一次,因此每行的值都不同。

因此它可以返回多行,或者根本不返回任何行。

这正是你在说的时候应该期待的,这个表达式会计算每一行。

显然你真正想要的是一个每个查询评估一次的表达式。

答案 1 :(得分:0)

newID将始终每行调用一次,如果您需要使用类似的唯一值:

ROUND(((3) * RAND() + 1), 0)