2016-12-01 56 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