2016-12-01 48 views
0

當我用下面的查詢工作:不給自己留多案例子查詢結果使用IN

SELECT * 
FROM TableA a 
WHERE a.FieldA IN (

    CASE 

    --select subquery returns a single value 
    WHEN a.FieldB = 'Value1' 
     THEN (select b.ID from TableB b where b.FK_Field = '123') 

    --select subquery returns multiple values 
    WHEN a.FieldB = 'Value2' 
     THEN (select c.ID from TableC c where c.FK_Field = '123') 

    END 
) 

第一種情況SELECT語句只返回一個b.ID。如果我只是有這樣的說法,我的代碼就可以工作。

但是,第二種情況說明會返回多個c.ID s。當我再補充一點檢查,我得到以下錯誤:

Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

如果我不得不WHERE a.FieldA =,那麼據我所知,子查詢只能返回值爲1。然而,我有WHERE a.FieldA IN,那爲什麼它會抱怨如果有多個值返回?

我該如何實施這種檢查?

+2

T-SQL中的CASE是一個**表達式**(如'a + b'),它返回一個**單一的原子值** - 您無法**使用它來選擇性地運行SQL片段返回整個結果集... –

+0

@marc_s我想那麼這個錯誤將落入類*當子查詢用作表達式*。你有什麼建議,我可以做這種檢查,而不使用'CASE'或以某種方式允許多個結果? –

+0

也許[這](http://stackoverflow.com/a/10260297/92546)答案將提供一些見解。 – HABO

回答

0

由於@marc_s在註釋中解釋說:

CASE in T-SQL is an expression (like a+b) which returns a single, atomic value - you cannot use it to selectively run SQL snippets that return entire result sets

爲了解決這個錯誤,我刪除了CASE語句,並用來代替一堆ANDOR語句來完成同樣的一種檢查。

SELECT * 
FROM TableA a 
WHERE 
    (a.FieldB = 'Value1' 
     AND a.FieldA IN (select b.ID from TableB b where b.FK_Field = '123')) 

    OR (a.FieldB = 'Value2' 
     AND a.FieldA IN (select c.ID from TableC c where c.FK_Field = '123')) 

此代碼是有點混亂比CASE聲明,但它的作品。

+0

也可以從索引中受益。 – Deadsheep39

0

許多這樣做的方法,下面是一個使用UNION ALL的一種方法和相關EXISTS聲明

;WITH cte AS (
    SELECT 'Value1' as FieldB, b.Id 
    FROM 
     TableB 
    WHERE 
     b.FK_FieldId = '123' 

    UNION ALL 

    SELECT 'Value1' as FieldB, c.Id 
    FROM 
     TableB 
    WHERE 
     c.FK_FieldId = '123' 
) 



SELECT * 
FROM 
    TableA a 
WHERE 
    EXISTS (SELECT 1 
      FROM 
      cte c 
      WHERE 
      a.FieldB = c.FieldB 
      AND a.FieldA = c.Id) 

與你所寫的方法的問題是,你得到一個非標量值(意思是多於1行)其中sql期望標量值。在表達式中,只有標量值可用於THEN部分以及WHEN中的一些規則。要解決您需要將您的案例表達式拆分爲多個where語句和/或使用其他技術(如上面的一個)。

或者你可以寫你的情況表達這樣的:

SELECT * 
FROM 
    TableA a 
WHERE 
    (CASE 
     WHEN a.FieldB = 'Value1' AND a.FieldA IN (select b.ID from TableB b where b.FK_Field = '123') THEN 1 
     WHEN a.FieldB = 'Value2' AND a.FieldA IN (select c.ID from TableC c where c.FK_Field = '123') THEN 1 
     ELSE 0 
    END) = 1 
+0

您提出的兩種方法在效率上有差異嗎?或者還有其他一些原因,你會建議我使用另一種? –

+1

這將取決於您的數據集,但實際將其切換爲單獨使用可能會更快。 http://stackoverflow.com/questions/173041/not-in-vs-not-exists你發佈的答案,以及可能會更快,但你不得不測試。當使用IN時,如果非常重要的是列表不能包含NULL,這在使用ID時可能不會成爲問題,但如果由於某種原因它確實包含NULL,則會得到所有匹配,因爲SQL無法比較null – Matt

+0

感謝建議。我會做一些測試,看看哪種方法最適合我的數據集。 –

0

不要在謂詞中使用case如果沒有必要 - 使用的情況下讓你的論點non-SARG(你QRY不會使用索引)。

SELECT * 
FROM TableA a 
WHERE EXISTS(
     SELECT NULL 
     FROM TableB b 
     WHERE a.FieldB = 'Value1' 
      AND b.FK_Field = '123' 
      AND a.FieldA = b.ID)) 
    OR EXISTS(
     SELECT NULL 
     FROM TableC c 
     WHERE a.FieldB = 'Value2' 
      AND c.FK_Field = '123' 
      AND a.FieldB = c.ID)) 

使用索引。並嘗試讓你的qry可讀。

或者,如果你想使用半聯接:

SELECT * 
FROM TableA a 
WHERE a.FieldA IN (
     SELECT b.ID 
     FROM TableB b 
     WHERE a.FieldB = 'Value1' 
      AND b.FK_Field = '123')) 
    OR a.FieldB IN (
     SELECT c.ID 
     FROM TableC c 
     WHERE a.FieldB = 'Value2' 
      AND c.FK_Field = '123')) 

這兩種解決方案都與特區政府。

+1

你能解釋SARG是什麼嗎?我認爲'CASE'語句可以提高效率,因爲一旦發現匹配,語句就會終止? –

+0

SARG意味着搜索參數。它對於製造有效的問題非常重要。不久,這意味着讓你的qry能夠使用索引或最有效的執行計劃。 – Deadsheep39