【发布时间】:2021-12-13 21:31:15
【问题描述】:
我在尝试使用 RAND 和 CHOOSE 函数在 T-sql 中生成随机值时遇到了一个有趣的 SQL 服务器行为。
我的目标是尝试使用 RAND() 作为 rng 返回两个给定值之一。很简单吧?
对于那些不知道的人,CHOOSE 函数接受一个索引号 (int) 以及一组值,并返回指定索引处的值。很简单。
第一次尝试我的 SQL 看起来像这样:
select choose(ceiling((rand()*2)) ,'a','b')
令我惊讶的是,这个表达式返回了三个值之一:null、'a' 或 'b'。由于我没想到我开始挖掘空值。 RAND() 函数返回一个范围从 0(包括)到 1(不包括)的浮点数。因为我将它乘以 2,所以它应该返回从 0(包括)到 2(不包括)范围内的任何值。因此使用 CEILING 函数后的最终值应该是以下之一:0,1,2。在意识到我通过'c'扩展了值列表以检查是否可能会返回。我还查看了CEILING 的文档页面并了解到:
返回值与 numeric_expression 的类型相同。
我假设 CEILING 函数返回 int,但在这种情况下,这意味着该值在用于 CHOOSE 之前被隐式转换为 int,这在文档页面上确实有说明:
如果提供的索引值具有非 int 的数值数据类型, 然后将值隐式转换为整数。
以防万一我添加了明确的演员表。我的 SQL 查询现在看起来像这样:
select choose(cast(ceiling((rand()*2)) as int) ,'a','b','c')
但是,结果集没有改变。为了检查哪些值导致了问题,我尝试预先生成该值并在 CHOOSE 结果旁边选择它。它看起来像这样:
declare @int int = cast(ceiling((rand()*2)) as int)
select @int,choose( @int,'a','b','c')
有趣的是,现在结果集更改为 (1,a), (2,b),这是我最初的目标。在深入研究 CHOOSE 文档页面并进行一些测试后,我了解到在以下两种情况之一中会返回“null”:
- 给定索引为空
- 给定的索引超出范围
在这种情况下,这意味着在 SELECT 语句中生成的索引值是 0 或大于 2/3(我假设这里不可能有负数,并且从 1 开始选择函数索引)。正如我之前所说,0 应该是以下可能性之一:
ceiling((rand()*2))
,但由于某种原因,它永远不会为 0(至少当我像这样尝试 100 万次以上时)
set nocount on
declare @test table(ceiling_rand int)
declare @counter int = 0
while @counter<1000000
begin
insert into @test
select ceiling((rand()*2))
set @counter=@counter+1
end
select distinct ceiling_rand from @test
因此我假设 SELECT 中生成的值大于 2/3 或 NULL。为什么只有在 SELECT 语句中生成时才会这样?也许在 SELECT 中解析 CAST、CELING 或 RAND 的顺序与看起来不同?确实我只尝试了有限的次数,但在这一点上,它成为统计波动的可能性非常小。它在某种程度上是一个浮点错误吗?我真的很迷茫,期待任何解释。
TL;DR:在 SELECT 语句中生成随机数时,可能值的结果集与在 SELECT 语句之前生成时不同。
干杯, 南加州大学
编辑:格式化
【问题讨论】:
标签: sql-server tsql random rounding