2013-10-23 76 views
2

我在SQL Server中這個簡單的表:CASE在sql服務器 - 實現澄清?

DECLARE @tbl table(a int , b NVARCHAR(100), isCalcByA bit) 

INSERT INTO @tbl 
SELECT 1,'c',1 
UNION ALL 
SELECT 2,'d',0 

確定。

如果我運行此:

SELECT CASE 
      WHEN isCalcByA = 1 THEN a 
      ELSE b 
     END FROM @tbl 

它會產生一個錯誤:

消息245,級別16,狀態1,9號線
轉換轉換爲nvarchar值 'd' 時失敗到數據類型int。

可以理解爲什麼它正在發生的事情:

因爲它被積累的數據(顯示)在同一列不能將兩者intstring

但對於這一點:

SELECT 'dummy' 
FROM @tbl 
WHERE CASE 
      WHEN isCalcByA = 1 THEN a 
      ELSE b 
     END IS NOT NULL 

這裏 -

  • 總是顯示string
  • 我不積累不同顯示不同類型的結果。
  • 我正在檢查他們對not null而不是stringint值。

但是我仍然得到相同的錯誤。

我錯過了什麼?

NB

我知道我能/應該這樣做:

SELECT 'dummy' 
FROM @tbl 
WHERE 
(isCalcByA = 1 AND a IS NOT NULL) 

OR 

(isCalcByA <> 1 AND b IS NOT NULL) 

(工作正常)

但我問爲什麼沒有在第一CASE工作情況

+0

'CASE WHEN isCalcByA = 1 then ELSE b END IS NOT NULL'永遠不會是false - case語句不能返回null。 – Bridge

+0

@Bridge「永不假」不是錯誤的原因。這就像'where 1 = 2'(這當然不是錯誤) –

+0

我同意 - 只是說where子句是毫無意義的,因爲它永遠是相同的值,是真的。 – Bridge

回答

3

CASE表達 - 它返回一個特定類型的。它可能返回的所有可能值都必須轉換爲某種常見類型。系統使用類型優先規則來考慮所有可能的返回值的類型,並確定該類型是什麼。 int具有更高的優先權並獲勝。

CASE 
     WHEN isCalcByA = 1 THEN CONVERT(nvarchar(100),a) 
     ELSE b 
    END 

會起作用,因爲現在所選的常見類型是明確的nvarchar(100)

+0

有趣....但是,你的意思是_int具有更高的優先級?你能提供小樣本嗎? (這將在X中工作,而不是與Y一起工作)? –

+1

@RoyiNamir - [數據類型優先順序](http://technet.microsoft.com/en-us/library/ms190309.aspx):「當運算符組合兩個不同數據類型的表達式時,數據類型優先級的規則指定將優先級較低的數據類型轉換爲優先級較高的數據類型「 –

+0

,以便sql引擎遍歷(運行時)所有不同類型的相關列,並採用具有較高優先級的數據類型並嘗試(**什麼時候?在什麼階段?這是否意味着它首先掃描所有行,然後決定?**)將它們轉換爲所有最高的先驗值? –

0
DECLARE @tbl table(a int , b NVARCHAR(100), isCalcByA bit) 

INSERT INTO @tbl 
SELECT 1,'c',1 
UNION ALL 
SELECT 2,'d',0 
UNION ALL 
SELECT null,'d',1 


SELECT CASE 
      WHEN isCalcByA = 1 THEN CAST(a AS VARCHAR(30)) 
      ELSE b 
     END FROM @tbl 

A bove,您將根據選擇選擇兩種不同的數據類型。

SELECT 'dummy' 
FROM @tbl 
WHERE CASE 
      WHEN isCalcByA = 1 THEN CAST(a AS VARCHAR(30)) 
      ELSE b 
     END IS NOT NULL 

以上,你是選擇 '假' 每一次,不管條件。

因此,在第一個聲明中,您將基於case設置return type,並且該案例可以返回two different types。在第二個查詢中,返回類型始終爲same type

+0

我不知道你讀了我的問題怎麼樣 - 通過你的第二個代碼也失敗了。 –

+0

@RoyiNamir,我只是在看你在選擇什麼...應該更加註意。我已經更新了答案。 – christiandev

1

無論您在SELECTWHERE條款中使用CASECASE表達式應始終返回相同的數據類型。因此,兩列轉換爲可容納兩個數據類型:

CASE 
    WHEN isCalcByA = 1 THEN CAST(a AS NVARCHAR(100)) 
    ELSE b 
END 

CASE expression文檔:

返回從result_expressions類型的集合和可選else_result_expression最高優先級的類型。

當各種WHENELSE部分具有不同的數據類型作爲結果,最高優先級被從該列表中選擇的:Data Type Precedence和所有結果被轉換爲數據類型。

您的查詢失敗,因爲int優先於nvarchar

+1

'SELECT CASE當1 = 1那麼1 ELSE'f'結束' - 工作和它不是相同的類型 –

+2

@RoyiNamir - 真,但如果1 = 1曾經是假的,它仍然會*嘗試*將f''轉換爲int並失敗。你是否期望它插入特殊情況下處理字面字符串,永遠不能轉換與更常用的字符串可能會在每行上變化,可能確實是可轉換的? –

0

不要想像CASE它是從正常語言內置IF。它更像是具有強打字的... ? ... : ...運算符 - 它必須導致特定的單數類型。如果你想混合你需要將它轉換爲nvarchar的列。

你也可以想想,SELECT的結果應該可以由CREATE TABLE來定義。