Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
921 views
in Technique[技术] by (71.8m points)

sql server 2008 - Sql Case statement within Sql IN

I have a sql statement which have a where condition like below

WHERE (TABLE1.Field=@Id OR (@Id=0))

This works fine when @Id values is zero or more

Now I need to match multiple values when @Id value become 0.

I tried something like below which doesn't work. I know that I can use IF-ELSE in here, but wonder whether this can be achieve using case statemet

WHERE TABLE1.Field IN (CASE WHEN @Id>0 THEN @Id ELSE (6,16,18) END)
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can do it using an OR:

WHERE   (@Id > 0 AND Table1.Field = @Id)
OR      (@Id = 0 AND Table1.Field IN (6,16,18))

However, I would advise using (as you have said) IF/ELSE, when mashing together two conditions like this you can often force suboptimal plans. e.g In your example you could simplify this to a schema as follows:

CREATE TABLE T
(   ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY, 
    Field INT NOT NULL, 
    SomeOtherField INT NULL
);
GO
INSERT T  (Field)
SELECT  Number
FROM    Master..spt_values
        CROSS JOIN (VALUES (1), (2), (3)) t (A)
WHERE   Type = 'P'
GO
CREATE NONCLUSTERED INDEX IX_T_Field ON T (Field) INCLUDE (SomeOtherField);

This simply populates one of the columns with the numbers 0-2047 repeated 4 times each (just for some example data). Then If I create two procedures, one that uses 'IF/ELSE' one that combines the criteria as above:

CREATE PROCEDURE dbo.Test @ID INT
AS
    SELECT  ID, Field, SomeOtherField
    FROM    T
    WHERE   (@Id > 0 AND T.Field = @Id)
    OR      (@Id = 0 AND T.Field IN (6,16,18))

GO
CREATE PROCEDURE dbo.Test2 @ID INT
AS
    IF @ID = 0
        SELECT  ID, Field, SomeOtherField
        FROM    T
        WHERE   T.Field IN (6, 16, 18)
    ELSE
        SELECT  ID, Field, SomeOtherField
        FROM    T
        WHERE   T.Field = @Id
GO

Since compilation of queries will only happen once (unless you explicitly say otherwise), the optimiser will not pick a different plan depending on whether you pass 0 or pass an ID > 0 to the procedure, so both of the following:

EXECUTE dbo.Test 0;
EXECUTE dbo.Test 1;

Will give this plan:

enter image description here

The second procedure is able to estimate the best execution plan much better so running this:

EXECUTE dbo.Test2 0;
EXECUTE dbo.Test2 1;

Gives the following plan:

enter image description here

Real world examples will obviously vary, and I have deliberately constructed an example that proves my point. It is slightly more effort to duplicate a lot of code by using IF/ELSE, but it is often worth it.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...