SQL 服务器模函数似乎 return 值超出预期范围

SQL Server Modulo function appears to return values outside of expected range

在 case 语句中 运行 取模函数时,很多时候会 return 计算超出预期范围的值。

SELECT CASE WHEN ABS(CheckSUM(NewId())) % 5 IN (0,1,2,3,4) then NULL 
       ELSE 'What Happened?' END 

如果您 运行 这个脚本几次,您会发现有时结果似乎超出了 0,1,2,3,4 的范围。我的想法是,这在某种程度上 return 在 case 语句中使用非整数值导致模数成为按大小写排序的无效方法。

有人可以解释一下这些情况下发生了什么,以便我将来可以解决这个问题吗?

注意:如果我 运行 代码模函数本身(在 case 语句之外)和 return 结果所有值都在 0,1,2,3,4 范围内不出所料。

我知道这不是答案,但我把它放在这里是为了正确格式化代码。

当我运行 OP的代码时,我第三次得到了"What Happened?"。我想看看发生这种情况时返回了什么 Modulo,看看它可能提供什么洞察力,但要做到这一点,我需要一个不动的 NewID(),所以我 t运行 提出了他的查询:

DECLARE @NewID UNIQUEIDENTIFIER = NEWID();

SELECT CASE WHEN ABS(CheckSUM(@NewID)) % 5 IN (0,1,2,3,4) then NULL 
       ELSE 'What Happened?' END 

而且我没有看到任何 "What Happened?"。

我最好的猜测是 SQL 引擎试图以某种方式使操作顺序变得可爱,有时最终会在非数字分子上进行模运算。

但是如果不能在 t运行 提出的查询上重现行为,就不可能 "catch it in the act"。

为了使这个成为一个实际的答案,我想将来要解决这个问题,如果可能的话,你应该用 NEWID() 填充一个变量而不是将它内嵌在你的查询中。

其他形式的 "un-nesting" 这些操作也可能有效。

这是我在执行 Mikael 的查询时看到的执行计划:

将您的声明更改为

SELECT top(1)
 CASE WHEN ABS(CheckSUM(NewId())) % 5 IN (0,1,2,3,4) then NULL 
       ELSE 'What Happened?' END 

并查看实际的执行计划。

案例中的IN部分展开为。

CASE WHEN abs(checksum(newid()))%(5)=(4) OR 
          abs(checksum(newid()))%(5)=(3) OR 
          abs(checksum(newid()))%(5)=(2) OR 
          abs(checksum(newid()))%(5)=(1) OR 
          abs(checksum(newid()))%(5)=(0) 
  THEN NULL 
  ELSE 'What Happened?' 
END

abs(checksum(newid()))%(5) 对 in 子句中的每个值执行一次。