我正在尝试生成一些随机数据,并且我一直在使用newid()来播种函数,因为它为每一行调用一次并保证每次都返回不同的结果。但是,我经常得到的值在某种程度上不等于预期范围内的任何整数。
我尝试了一些变体,包括highly upvoted one,但它们都会导致同样的问题。我把它放到一个显示问题的脚本中:
declare @test table (id uniqueidentifier)
insert into @test
select newid() from sys.objects
select
floor(rand(checksum(id)) * 4),
case isnull(floor(rand(checksum(id)) * 4), -1)
when 0 then 0
when 1 then 1
when 2 then 2
when 3 then 3
when -1 then -1
else 999
end,
floor(rand(checksum(newid())) * 4),
case isnull(floor(rand(checksum(newid())) * 4), -1)
when 0 then 0
when 1 then 1
when 2 then 2
when 3 then 3
when -1 then -1
else 999
end
from @test
我希望所有四列的结果总是在0到3的范围内。当从表中检索唯一标识符时,结果总是正确的(前两列。)同样,当它们在运行时输出时它们也是正确的(第三列。)但是当它们在飞行中进行比较时对于case语句中的整数,它通常会返回一个超出预期范围的值。
这是一个例子,这是我刚刚运行时的前20行。正如您所看到的,最后一列中有'999'实例不应该存在:
0 0 3 1
3 3 3 1
0 0 3 3
3 3 2 999
1 1 2 999
3 3 2 1
2 2 0 999
0 0 0 0
3 3 2 0
1 1 3 999
3 3 0 999
2 2 2 2
1 1 3 0
2 2 3 0
3 3 1 999
0 0 1 999
3 3 1 1
0 0 0 3
3 3 0 999
0 0 1 0
起初我认为类型强制可能与我预期的不同,而rand()* int的结果是浮点数不是int。所以我把它全部包裹在地板上以迫使它成为一个int。然后我想也许有一个奇怪的空值爬行,但是在我的case语句中,null将返回为-1,并且没有。
我运行了这两个不同的SQL Server 2012 SP1实例,两者都给出了相同的结果。
答案 0 :(得分:2)
在第四列中,isnull(floor(rand(checksum(newid())) * 4), -1)
每行最多评估五次。一次针对案件的每个分支。在每次调用时,值可以不同。所以它可以返回2,不匹配1,3不匹配2,1不匹配3,3不匹配4落入其他并返回999。
如果您获得执行计划,并查看XML,可以看到这一行[添加了空格。]:
<ScalarOperator ScalarString="
CASE WHEN isnull(floor(rand(checksum(newid()))*(4.000000000000000e+000)),(-1.000000000000000e+000))=(0.000000000000000e+000) THEN (0)
ELSE CASE WHEN isnull(floor(rand(checksum(newid()))*(4.000000000000000e+000)),(-1.000000000000000e+000))=(1.000000000000000e+000) THEN (1)
ELSE CASE WHEN isnull(floor(rand(checksum(newid()))*(4.000000000000000e+000)),(-1.000000000000000e+000))=(2.000000000000000e+000) THEN (2)
ELSE CASE WHEN isnull(floor(rand(checksum(newid()))*(4.000000000000000e+000)),(-1.000000000000000e+000))=(3.000000000000000e+000) THEN (3)
ELSE CASE WHEN isnull(floor(rand(checksum(newid()))*(4.000000000000000e+000)),(-1.000000000000000e+000))=(-1.000000000000000e+000) THEN (-1)
ELSE (999)
END
END
END
END
END
">
将表达式放在CTE中似乎可以防止重新计算:
; WITH T AS (SELECT isnull(floor(rand(checksum(newid())) * 4), -1) AS C FROM @Test)
SELECT CASE C
when 0 then 0
when 1 then 1
when 2 then 2
when 3 then 3
when -1 then -1
else 999 END
FROM T