【问题标题】:CHECKSUM(NewId()) executes multiple times per rowCHECKSUM(NewId()) 每行执行多次
【发布时间】:2012-02-27 11:06:45
【问题描述】:

在查看代码时,我遇到了一些奇怪的事情,有人读到您可以使用 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(这是程序员在编写代码时使用的“测试”)

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

【问题讨论】:

  • 我不明白你在说什么。您是说他们选择校验和路由是因为 RAND 没有按行评估,现在您要问为什么它按行评估?
  • @LasseV.Karlsen - 看起来他们需要每个外部查询行评估一次但在传递到子查询时稳定的东西。 SELECT *, (SELECT country FROM @Values v WHERE v.id = CA.id) FROM @Values CROSS APPLY (SELECT ABS(CHECKSUM(NewId())) % 3 + 1) CA(id) 会这样做,但不保证会这样做。
  • @MartinSmith 如果您将其发布为答案,我会接受。
  • SELECT *, (SELECT country FROM @Values v WHERE v.id = CA.id) FROM @Values CROSS JOIN (SELECT ABS(CHECKSUM(NewId())) % 3 + 1) CA(id) 也可以工作(可能与 CROSS APPLY 一样(非)可靠)。

标签: sql sql-server random newid


【解决方案1】:

为什么,这是意料之中的。

ABS(CHECKSUM(NewId())) % 3 + 1@Values 中的每一行计算一次,因此每一行都有不同的值。

所以它可以返回多行,或者根本不返回行。

这正是您在说“此表达式对每一行进行计算”时所期望的。

显然,您真正想要的是每个查询计算一次的表达式。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 每次查询执行只计算一次它的值,CROSS APPLY (SELECT ABS(CHECKSUM(NewId())) % 3 + 1) CA(id) 正如@Martin 所指出的,将为父表中的每一行提供一个唯一值,而不是每次在内部查询中调用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-23
    • 2021-07-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多