2013-09-27 43 views
0

我需要做这样的事情:
T-SQL - 结合IN子句和CASE语句

select * 
from Table 
     inner join Few more tables 
where t2.ID IN(case when @Param1 = 0 then 
         (select ID FROM tbl10 WHERE ForeignKey = @param2) 
       else @Param1 end) 

所以如果@参数1是0的话,我想要一些设定值是一个匹配(基于@ param2),如果它不是0,我只需要@Param1匹配。 我已经尝试了几个语法变体,但它不会工作。

我也见过类似的问题,但它并没有帮助我。

回答

1

你可以试试这个

select * from Table 
inner join Few more tables 
where t2.ID IN(select case when @Param1 = 0 then ID else @Param1 end FROM tbl10 WHERE ForeignKey = @param2) 
+0

这个查询将无法正常工作。如果tabl10不返回@ param2的任何行怎么办? –

+0

如果'@Param2'没有返回行,那么除了不工作,即使您不关心结果,当@Param1为0时,您也会在tbl10上强制进行表扫描。这不是一个有效的解决方案。 – GarethD

4

使用OR

SELECT * 
FROM table1 t1 
     INNER JOIN table2 t2 
       ON t1.t1col = t2.t1col 
WHERE (@param1 <> 0 AND t2.id = @Param1) 
    OR (@param1 = 0 AND t2.id IN (SELECT id 
          FROM tbl10 
          WHERE foreignkey = @param2)) 
0

简单有效:

select * from Table 
inner join Few more tables 
where 

    -- IF 
    @Param1 = 0 
     and t2.id in (select ID FROM tbl10 WHERE ForeignKey = @param2) 

    -- ELSE 
    or t2.id = @Param1 
+0

这仍然会处理't2.id = @ Param1'为0'@ param1'值 –

+0

@rs。如果AND IN变为假,也可以评估't2.id = @ Param1'。只要利用没有ID为0的事实。身份从1开始,因此't2.id = 0'将始终为假。然而,恳求承认,这个简单的答案在Steve Jobs(他是[Employee Number 0](http://www.dazeinfo.com/2011/01/19/10-amazing-facts-about) -steve-jobs /))的工作,在这种情况下,我会和@ TimSchmelter的答案一起去。否则,这个答案就足够了 –

+0

我同意你所说的,我指的是你的IF,ELSE评论说它实际上不是一个IF ELSE,因为两者都得到评估 –

1
SELECT * 
FROM Table 
     JOIN Few more tables 
WHERE t2.ID IN (
    SELECT @Param1 WHERE @Param1 <> 0 
    UNION ALL 
    SELECT ID FROM tbl10 WHERE ForeignKey = @param2 AND ISNULL(@Param1, 0) = 0 
    ) 
1

我会完全不同处理这和使用IF/ELSE。通过将不同基数与不同基数混合在一起,可以降低优化者选择最佳查询计划的几率。你会得到更好的表现使用这样的事情:

IF @Param = 0 
    BEGIN 
     SELECT * 
     FROM T 
     WHERE A IN (SELECT TID FROM T2 WHERE ID = 1 @param2); 
    END 
ELSE 
    BEGIN 
     SELECT * 
     FROM T 
     WHERE ID = @Param1; 
    END 

它看起来像更多的代码,所以应该效率较低,但实际上不是。使用这个测试场景:

CREATE TABLE T (ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, A INT NOT NULL, B INT NULL); 
INSERT T (A, B) 
SELECT A, Number 
FROM ( SELECT TOP 1000 A = RANK() OVER(ORDER BY a.object_id) 
      FROM sys.all_objects a 
     ) a 
     CROSS JOIN (VALUES (1), (2), (3)) n (Number); 

CREATE TABLE T2 (ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, TID INT NOT NULL); 
INSERT T2 (TID) 
SELECT T.ID 
FROM T 
     CROSS JOIN (VALUES (1), (2), (3)) n (Number); 

CREATE NONCLUSTERED INDEX IX_T_A ON T (A); 
CREATE NONCLUSTERED INDEX IX_T2_TID ON T2 (TID); 
GO 
CREATE PROCEDURE dbo.Proc1 @Param1 INT, @Param2 INT 
AS 
    SELECT ID, A, B 
    FROM T 
    WHERE (@param1 <> 0 AND t.A = @Param1) 
     OR (@param1 = 0 AND t.A IN(SELECT TID FROM T2 WHERE ID = @param2)); 

-- (SORRY TIM, BUT YOURS WAS THE BEST OF THE REST) 
GO 

CREATE PROCEDURE dbo.Proc2 @Param1 INT, @Param2 INT 
AS 
    IF @Param1 = 0 
     BEGIN 
      SELECT ID, A, B 
      FROM T 
      WHERE A IN (SELECT TID FROM T2 WHERE ID = @param2); 
     END 
    ELSE 
     BEGIN 
      SELECT ID, A, B 
      FROM T 
      WHERE A = @Param1; 
     END 

GO 

如果运行的第一个程序(不带IF),因为SQL-Server不知道什么@参数1和@参数2将在编译的时候,它不知道哪些条件见面会,所以不能相应地优化,所以创造了两个条件

EXECUTE dbo.Proc1 1, 1; 
EXECUTE dbo.Proc1 0, 1; 

enter image description here

同样的计划反之,如果你使用SQL IF/ELSE - 服务器可以为每个条件下的最优方案:

EXECUTE dbo.Proc2 1, 1; 
EXECUTE dbo.Proc2 0, 1; 

enter image description here

在这种情况下的实际影响并不如查询计划建议的那样糟糕,因为SQL-Server是运行时不够聪明,不评估subuqery如果@Param1 = 0从T2选择,并我并不是说永远不会有使用多个条件的情况,但通常情况下,当您有一个影响所需谓词的常量时,最好将它与IF/ELSE分开,而不是将两个谓词混合在一起。

有时较少的代码并不总是更高效的查询。

DDL and queries on SQL-Fiddle

0

我喜欢与IF语句的方法,但是如果你不喜欢它,试试这个,与UNION ALL

SELECT * 
FROM table1 t1 
    INNER JOIN table2 t2 
      ON t1.t1col = t2.t1col 
WHERE @param1 <> 0 AND t2.id = @Param1 
UNION ALL 
SELECT * 
FROM table1 t1 
    INNER JOIN table2 t2 
      ON t1.t1col = t2.t1col 
    INNER JOIN tbl10 
      ON t2.ID =tbl10.ID 
      AND foreignkey = @param2 
WHERE @param1 = 0