2015-09-09 42 views
7

我使用SQL Server 2012的T-SQL Case語句使用NEWID()作爲隨機源怪異行爲

如果我下面來獲得隨機十歲上下的號碼清單範圍在[1,3 ],它工作得很好:

SELECT TOP 100 
    ABS(CHECKSUM(NEWID()))%3 + 1 [value_of_rand] 
FROM sys.objects 

,我得到的好東西像這樣(所有的1和3之間)。

3 
2 
2 
2 
1 
....etc. 

但是,如果我再放入相同的鏈接隨機值函數的輸出轉換爲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 

我在做什麼錯在這裏?

+0

難道呈現當數它的舍入,但評估時,是不是?嘗試把它放在一個變量和調試/拋出你有「這很奇怪」的地方。 – Dane

+0

如果我將它賦值給一個變量,然後對該變量執行case語句,那麼它工作正常,沒有奇怪。不過,我認爲你的調試方法是不可能的。我收到一條錯誤消息,說:「將一個值賦給一個變量的SELECT語句不能與數據檢索操作組合在一起。」如果我嘗試這樣做: DECLARE @a int;選擇@a =(ABS(校驗和(newid()))%3 + 1),CASE @a \t當1然後'1'當2當'兩'\t當3然後'三'\t ELSE'奇怪:'+ (@a as varchar(max))END – Anssssss

+0

您可以隨時刪除整個'WHEN 3'的情況,並將'three'放入'ELSE'中 –

回答

7

您:

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 clause結束。所以沒有錯誤或捕獲,只是你使用它的變量表達式,這是完全正常的行爲。

這就好比COALESCE syntactic-sugar

coalesce表達式相同的類是CASE 表達式句法快捷方式。也就是說,代碼COALESCE(表達式1,... n)由查詢優化器重寫 如下面的情況表達式:

CASE

WHEN(表達式1 IS NOT NULL)THEN表達式

WHEN (expression2 IS NOT NULL)THEN expression2

...

ELSE expressionN

END

這意味着,輸入值(表達式1,表達式2, expressionN等)將被評估多次。另外,在符合SQL標準 ,中,包含 子查詢的值表達式被認爲是非確定性的,並且子查詢被評估爲 兩次。無論哪種情況,可以在 第一次評估和隨後的評估之間返回不同的結果。

編輯:

解決方案: SqlFiddle

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) 
+0

@Assssss查看我更新的答案,有解決方案 – lad2025

+0

查看[CASE](https://msdn.microsoft.com/en-us/library/ms181765.aspx)的頁面,它表示,在** Simple CASE表達式下:**「評估* input_expression *,然後按照指定的順序,對每個WHEN子句計算* when_expression *時的input_expression * = *。」這可能是一個文檔錯誤,並且您的解釋似乎是正確的,但是您是否看到查詢實際上一旦提交就被重寫了?只是好奇。 –

+0

@srutzky關於'COALESCE'的連接錯誤存在,它被解包爲CASE WHEN,這是相同的情況。 'COALESCE'在文檔中有警告,'CASE'沒有:(請參閱https://connect.microsoft.com/SQLServer/feedback/details/546437/ – lad2025

1

我不能告訴你爲什麼,這確實很奇怪,但我可以給你一個解決方法。努力沒有更多使用它們

;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 

之前選擇隨機值成CTE「這是奇怪的」

+0

這是行不通的,但我不確定它有多可靠。優化器可以隨意移動,並且可以在將來對它重新排序,但是,我懷疑只要你在CTE或子查詢中保留「TOP 100」表達式,它可能會停留穩定。 – RBarryYoung

+0

@Jamiec這就是爲什麼http://stackoverflow.com/a/32485383/5070879 – lad2025

2

我認爲您遇到這裏的問題是,(ABS(CHECKSUM(NEWID()))%3 + 1)不是一個值,它是一個表達式 SQL可以隨時重新評估它。你可以嘗試各種語法的東西,如刪除額外的括號或CTE。這可能會使其消失(現在),但它可能不會,因爲從邏輯上來說,它看起來像是對優化程序的相同請求。

我認爲唯一可靠的方法就是先保存它(保存到臨時表或真實表),然後使用第二個查詢來引用保存的值。

+0

但是,該表達式可能如何產生一個值,導致CASE語句的ELSE分支被擊中? – Anssssss

+1

@Assssss因爲'CASE' *表達式*不像C'switch'語句,所以可以在每個'CASE'條件測試中重新評估表達式,並得出不同的值。 – RBarryYoung

+0

我不明白。你是說(ABS(CHECKSUM(NEWID()))%3 + 1)能產生1,2或3以外的東西嗎? – Anssssss