简体   繁体   中英

T-SQL - Combine IN Clause and CASE statement

I need to do something like this:

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)

So if @Param1 is 0 then i want some set of values to be a match(based on the @param2),and if its not 0 i want just @Param1 to match. i have tried few syntax variations but it wont work.

I have also seen a similar question,but it didn't help me.

Use 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) ) 

can you try this

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

I would approach this completely differently and use IF/ELSE . By mashing together two different criteria with different cardinality you are lowering the optimisers chances of choosing the best query plan. You will get much better performance using something like this:

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

It looks like more code, so should be less efficient but it really isn't. Using this test scenario:

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 run the first procedure (with no IF), since SQL-Server doesn't know what @Param1 and @Param2 will be at compile time, it doesn't know which condition will be met, so can't optimise accordingly, so creates the same plan for both conditions

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

在此处输入图片说明

Whereas, if you use IF/ELSE SQL-Server can create the optimal plan for each condition:

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

在此处输入图片说明

In this instance the actual impact is not as bad as the query plans suggest, since SQL-Server is smart enough at run time not to evaluating the subuqery to select from T2 if @Param1 = 0 , and I am not saying there is never a case for using multiple OR conditions, but usually when you have a constant that affects the predicate you want it is best to separate it with an IF/ELSE rather than mashing together two predicates.

Sometimes less code is not always a more efficient query.

DDL and queries on SQL-Fiddle

Simple and effective:

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

I like and approach with IF statement, but if you don't like it, try this, with 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM