views:

358

answers:

6

I am using MSSQL 2005 Server and I have the following SQL query.

IF @CategoryId IN (1,2,3)
    BEGIN
     INSERT INTO @search_temp_table 
     SELECT * FROM (SELECT d.DataId, (SELECT [Name] FROM Category WHERE CategoryId = d.CategoryId) AS 'Category', d.Description, d.CompanyName, d.City, d.CategoryId,
       d.CreatedOn, d.Rank, d.voteCount, d.commentCount, d.viewCount
     FROM Data d 
       INNER JOIN Keyword k
        ON d.DataId = k.DataId
     WHERE FREETEXT(k.Keyword, @SearchQ) AND d.CategoryId=@CategoryId AND d.IsSearch=1 AND d.IsApproved=1 ) AS Search_Data
    END
    ELSE
     BEGIN 
      INSERT INTO @search_temp_table 
      SELECT * FROM (SELECT d.DataId, (SELECT [Name] FROM Category WHERE CategoryId = d.CategoryId) AS 'Category', d.Description, d.CompanyName, d.City, d.CategoryId,
        d.CreatedOn, d.Rank, d.voteCount, d.commentCount, d.viewCount
      FROM Data d 
        INNER JOIN Keyword k
         ON d.DataId = k.DataId
      WHERE FREETEXT(k.Keyword, @SearchQ) AND d.IsSearch=1 AND d.IsApproved=1 ) AS Search_Data
     END

In the above query I have the category condition,

d.CategoryId=@CategoryId

which is executed when any category is passed, if no category is passed then I am not considering category condition in where clause, To implement category condition only when if the category in (1,2,3) I have used If-Clause, but can't we do this in single where query?? that means just check if the values is there in the category (or if it's easy then we can only check for 1,2,3 values) then that condition will be applied else query will not consider the category condition.

Is there any way, using CASE, or NOT NULL statements ??

+2  A: 

If @CategoryId is NULL when you don't want to filter by it you can use the below condition...

ISNULL(@CategoryId, d.CategoryId) = d.CategoryId

So if it's NULL then it equals itself and wont filter

EDIT

I like Marc Miller's COALESCE example and you could use either and I really shouldn't comment on the performance of one verses the other but...

My gut tells me ISNULL should win out but have a look at some of the debates on this issue if you have nothing better to do (or if performance is REALLY critical in this query).

NOTE: If the d.CategoryId in the table can be NULL then this approach will fail and the CASE WHEN THEN approach elsewhere on this question should be used

Rich Andrews
This doesn't check whether @Category is in 1,2 or 3.
Sung Meister
Hmm - they way I read the question was that the values passed in could only be 1,2 or 3 and that was why they were in the IF statement. Either way it's a bloody good way of implementing optional filtering :)
Rich Andrews
This may also fail if the CategoryId in the table can be NULL
Tom H.
Good point - I've generally used this approach on IDENTITY columns so it's not been a problem for me in the past but I'll edit the post to point this out.
Rich Andrews
+1  A: 

Doing a ((@CategoryId IN (1,2,3) AND CategoryId=@CategoryId) OR NOT @CategoryId IN (1,2,3)) will check category id if it is 1, 2 or 3 otherwise it won't apply that filter.

    WHERE FREETEXT(k.Keyword, @SearchQ) AND ((@CategoryId IN (1,2,3) AND d.CategoryId=@CategoryId) OR NOT @CategoryId IN (1,2,3))  AND d.IsSearch=1 AND d.IsApproved=1 ) AS Search_Data
Duke of Muppets
+1  A: 

If category is in 1,2 or 3 then use the specified @CategoryId to filter or else don't by checking itself.

AND IsNull(d.CategoryId, 1) = case when @CategoryId in (1,2,3) then @CategoryId else IsNull(d.CategoryId, 1) end

This query also works when @Category or CategoryId is null And the "If" statement can go away.

Full query below

INSERT INTO @search_temp_table 
    SELECT * FROM (SELECT d.DataId, (SELECT [Name] FROM Category WHERE CategoryId = d.CategoryId) AS 'Category', d.Description, d.CompanyName, d.City, d.CategoryId,
                    d.CreatedOn, d.Rank, d.voteCount, d.commentCount, d.viewCount
    FROM    Data d 
            INNER JOIN Keyword k ON d.DataId = k.DataId
    WHERE   FREETEXT(k.Keyword, @SearchQ) 
      AND IsNull(d.CategoryId, 1) = case when @CategoryId in (1,2,3) then @CategoryId else IsNull(d.CategoryId, 1) end
      AND d.IsSearch=1 
      AND d.IsApproved=1 ) AS Search_Data

*WARNING: Make sure to check against execution plan whether above query is slower than using "if" statement.

Sung Meister
This may not return the correct data if the CategoryId in the table can be NULL
Tom H.
It should work now even though CategoryId is null
Sung Meister
+2  A: 

If the only difference is your where clause then you could do this:

d.CategoryId = COALESCE(@CategoryId, d.CategoryId)

Not sure why you need the IN clause (IN (1,2,3)) as you mentioned that your reason for checking for it is to make sure it isn't NULL. So this should work the way you described.

Mark J Miller
+1  A: 

Could do a LEFT JOIN in there, like this:

INSERT INTO @search_temp_table
SELECT  *
FROM    (
    SELECT d.DataId,
      c.[Name] as 'Category',
      d.Description, d.CompanyName, d.City, d.CategoryId,
      d.CreatedOn, d.Rank, d.voteCount, d.commentCount, d.viewCount
      FROM Data d 
            INNER JOIN Keyword k ON d.DataId = k.DataId
      LEFT JOIN Category c on c.CategoryId=d.CategoryId
       AND c.CategoryId=@CategoryId
      WHERE FREETEXT(k.Keyword, @SearchQ)
      AND d.IsSearch=1
      AND d.IsApproved=1
) AS Search_Data

you wouldn't need the if statement anymore either.

Also, it's very important that you have the c.CategoryId=@CategoryId within the LEFT JOIN, if you move it to the WHERE clause it will force the LEFT JOIN into an INNER JOIN.

Rorschach
+1  A: 

Similiar to marks answer you can do the following:

WHERE FREETEXT(k.Keyword, @SearchQ) 
  AND d.IsSearch=1 
  AND d.IsApproved=1 
  AND ((@CategoryId NOT IN (1,2,3)) OR (d.CategoryId = @CategoryId))
) AS Search_Data

This way you eliminiate the function call

Chris Lively