Sql server 以newid()为随机源的T-SQL Case语句奇怪行为
我正在使用SQL Server 2012 如果我执行以下操作以获得[1,3]范围内的随机ish数字列表,则效果很好:Sql server 以newid()为随机源的T-SQL Case语句奇怪行为,sql-server,tsql,Sql Server,Tsql,我正在使用SQL Server 2012 如果我执行以下操作以获得[1,3]范围内的随机ish数字列表,则效果很好: SELECT TOP 100 ABS(CHECKSUM(NEWID()))%3 + 1 [value_of_rand] FROM sys.objects 我得到这样的好东西(都在1到3之间) 但是如果我把同一个链式随机值函数的输出放到CASE语句中,它显然不会只生成值1,2,3 SELECT TOP 100 CASE (ABS(CHECKSUM(NEWID(
SELECT TOP 100
ABS(CHECKSUM(NEWID()))%3 + 1 [value_of_rand]
FROM sys.objects
我得到这样的好东西(都在1到3之间)
但是如果我把同一个链式随机值函数的输出放到CASE语句中,它显然不会只生成值1,2,3
SELECT TOP 100
CASE (ABS(CHECKSUM(NEWID()))%3 + 1)
WHEN 1
THEN 'one'
WHEN 2
THEN 'two'
WHEN 3
THEN 'three'
ELSE
'that is strange'
END [value_of_case]
FROM sys.objects
它输出:
three
that is strange
that is strange
one
two
...etc
我做错了什么?我不能告诉你为什么,这确实很奇怪,但我可以给你一个解决办法。在尝试使用之前,将随机值选择到cte中
;with rndsrc(value_of_rand) as
(
SELECT TOP 100
ABS(CHECKSUM(NEWID()))%3 + 1
FROM sys.objects
)
SELECT TOP 100
CASE value_of_rand
WHEN 1
THEN 'one'
WHEN 2
THEN 'two'
WHEN 3
THEN 'three'
ELSE
'that is strange'
END [value_of_case]
from rndsrc
不再是“这很奇怪”我认为您在这里遇到的问题是
(ABS(CHECKSUM(NEWID())%3+1)
不是一个值,它是一个表达式,SQL可以随时重新计算它。您可以尝试各种语法方法,例如删除额外的括号或CTE。这可能会使它消失(目前),但可能不会,因为从逻辑上讲,它看起来与优化器的请求相同
我认为唯一可靠且受支持的停止此操作的方法是先将其保存(保存到临时表或真实表),然后使用第二个查询引用保存的值。您的:
SELECT TOP 100
CASE (ABS(CHECKSUM(NEWID()))%3 + 1)
WHEN 1
THEN 'one'
WHEN 2
THEN 'two'
WHEN 3
THEN 'three'
ELSE
'that is strange'
END [value_of_case]
FROM sys.objects
实际执行:
SELECT TOP 100
CASE
WHEN (ABS(CHECKSUM(NEWID()))%3 + 1) = 1 THEN 'one'
WHEN (ABS(CHECKSUM(NEWID()))%3 + 1) = 2 THEN 'two'
WHEN (ABS(CHECKSUM(NEWID()))%3 + 1) = 3 THEN 'three'
ELSE 'that is strange'
END [value_of_case]
FROM sys.objects;
基本上,您的表达式是不确定的,并且每次都进行计算,因此您可以使用ELSE子句
。所以没有bug或陷阱,只需将其与变量表达式一起使用,这是完全正常的行为
这是同一个班级
合并表达式是这种情况下的一种语法捷径
表情。也就是说,代码COALESCE(表达式1,…n)被重写
由查询优化器按照以下CASE表达式执行:
案例
当(expression1不为NULL)时,则expression1
当(expression2不为NULL)时,则expression2
其他表达
结束
这意味着输入值(表达式1、表达式2、,
expressionN等)将进行多次评估。还有,在
符合SQL标准,一个包含
子查询被认为是不确定的,子查询将被计算
两次。在这两种情况下,两个数据库之间可以返回不同的结果
第一次评估和后续评估
编辑:
解决方案:
SELECT TOP 100
CASE t.col
WHEN 1 THEN 'one'
WHEN 2 THEN 'two'
WHEN 3 THEN 'three'
ELSE 'that is strange'
END [value_of_case]
FROM sys.objects
CROSS APPLY ( SELECT ABS(CHECKSUM(NEWID()))%3 + 1 ) AS t(col)
是不是在表示数字时,它是四舍五入的,而在计算数字时,它不是四舍五入的?试着把它放在一个变量中,然后调试/抛出你有“那很奇怪”的值。如果我把它赋给一个变量,然后对该变量执行case语句,它工作得很好,没有奇怪。然而,我认为你的调试方法是不可能的。我得到一个错误,说“一个为变量赋值的SELECT语句不能与数据检索操作相结合”;选择@a=(ABS(checksum(newid())%3+1),CASE@a当1时,然后选择'one'当2时,然后选择'two'当3时,然后选择'three'ELSE'奇怪:'+cast(@a作为varchar(max))end当3大小写时,您总是可以删除整个
,并将三个放在ELSE
中,但是该表达式怎么可能产生一个值,导致case语句的ELSE分支被命中呢?@ansssss因为大小写
表达式与Cswitch
语句不同,表达式可以在每个情况下重新计算
条件测试并得出不同的值..我不明白。你是说(ABS(CHECKSUM(NEWID())%3+1)可以产生除1、2或3以外的结果吗?@anssss不,我是说在WHEN 1
分支上,它可能计算为3,然后在WHEN 2
分支上,它可能计算为1。好的,我明白你现在的意思了。LAD205在视觉上解释得更好。这确实有效,但我不确定它有多可靠。优化器可以在它认为合适的时候移动东西,并且可以在将来重新排序,但是,我怀疑,只要在CTE或子查询中使用表达式保留TOP 100
,它可能会保持稳定。查看页面,在Simple CASE expression下显示:对输入表达式求值,然后按照指定的顺序对每个when子句的输入表达式=when表达式求值。“这可能是一个文档错误,您的解释似乎是正确的,但您是否看到,一旦提交,查询实际上正在以这种方式重写?只是好奇。@srutzky有一个关于COALESCE
的onconnect错误,它被作为CASE展开,而这是相同的情况COALESCE
在文档中有警告,CASE
没有:(请参见我查看的连接项,它是用于COALESCE
,而不是CASE
。此外,它只说了与COALESCE
文档所说的相同的事情(您在答案中链接的内容).我的意思是:有没有任何实际证据表明该表达式正在按照您的建议重写?我这样问是因为CASE
的文档表明,在简单的CASE场景中(就是这样),输入_表达式只计算一次。我知道该行为符合您的解释,但最好有更明确的;-)。哇!很好的解释。我在MSDN页面上为CASE添加了一个链接……可能不会持续很久,但也许它会让人免于我之前的困惑。@srutzky是的,医生确实这么说,但是。。。优化器发生了变化。SQL优化器有很大的回旋余地(比我所知道的任何其他语言都要灵活)。顺便说一句,优化器在这方面的限制/边界是什么,我们从来没有明确定义过。对于标量表达式,据我所知,没有办法确定它在做什么,因为查询计划实际上没有下降
SELECT TOP 100
CASE t.col
WHEN 1 THEN 'one'
WHEN 2 THEN 'two'
WHEN 3 THEN 'three'
ELSE 'that is strange'
END [value_of_case]
FROM sys.objects
CROSS APPLY ( SELECT ABS(CHECKSUM(NEWID()))%3 + 1 ) AS t(col)