SQL is not one of my strong suits. I have a SQL Server 2008 database. This database has a stored procedure that takes in eight int parameters. For the sake of keeping this q
best source for dynamic search conditions:
Dynamic Search Conditions in T-SQL by Erland Sommarskog
there are a lot of subtle implications on how you do this as to if an index can be used or not. If you are on the proper release of SQL Server 2008 you can just add OPTION (RECOMPILE)
to the query and the local variable's value at run time is used for the optimizations.
Consider this, OPTION (RECOMPILE)
will take this code (where no index can be used with this mess of OR
s):
WHERE
(@search1 IS NULL or Column1=@Search1)
AND (@search2 IS NULL or Column2=@Search2)
AND (@search3 IS NULL or Column3=@Search3)
and optimize it at run time to be (provided that only @Search2 was passed in with a value):
WHERE
Column2=@Search2
and an index can be used (if you have one defined on Column2)
.... Where field=case @isActive WHEN -1 THEN field ELSE @isActive END ....
Rather than using -1 to signify that you don't know or don't care, how about just using Null for that? Pretty much what it was made for. Then you could switch to a Bit rather than an Int.
Also, I'm sure TomTom will disagree, but I think using a CASE statement is the way to go for this stuff.
Your mileage may vary, but it seems that the query engine handles it a lot better than wrapping things in IsNull or having multiple OR statements, which can get rather messy as you start adding other conditions.
No matter which way you go, the execution plan is going to suffer a little bit depending on what you're passing in, but it shouldn't be TOO horrible.
The extra benefit of going with CASE statements is that you can add a bit of complexity without much extra code (versus going with a bunch of OR statements). Also, the first condition to match your criteria can prevent the extra evaluations, which isn't always the case when dealing with OR's...
So, for 8 optional parameters with -1 as the value use to ignore the search, what you end up with is something along the lines of:
WHERE
@Search1 = CASE WHEN @Search1 = -1 THEN @Search1 ELSE @Column1 END
AND @Search2 = CASE WHEN @Search2 = -1 THEN @Search1 ELSE @Column2 END
AND @Search3 = CASE WHEN @Search3 = -1 THEN @Search1 ELSE @Column3 END
AND @Search4 = CASE WHEN @Search4 = -1 THEN @Search1 ELSE @Column4 END
AND @Search5 = CASE WHEN @Search5 = -1 THEN @Search1 ELSE @Column5 END
AND @Search6 = CASE WHEN @Search6 = -1 THEN @Search1 ELSE @Column6 END
AND @Search7 = CASE WHEN @Search7 = -1 THEN @Search1 ELSE @Column7 END
AND @Search8 = CASE WHEN @Search8 = -1 THEN @Search1 ELSE @Column8 END
NOTE: As KM pointed out, the NULL method falls short if the columns you're working will can potentially have NULL values, since NULL=NULL won't evaluate properly. So, for fun, I changed my answer back to what the original poster requested, which is to use their own identifier for skipping the search.
The pattern (column = @param OR @param IS NULL)
will give you optional parameters. You can use NULLIF
to neutralize your -1
. Even better would be to allow null parameters, instead of using a magic number.
WHERE
(Customer.IsActive = NULLIF(@isActive, -1) OR NULLIF(@isActive, -1) IS NULL)
WHERE coalesce(active,1) = (CASE
WHEN @isActive = -1 THEN coalesce(active,1)
ELSE @isActive
END)
There is NOT an elegant way - all ways suck.
WHERE @isActive == -1 OR isActive = @isActive
is basically the only way - but even then you please make sure that the query plan is reevaluated every time, otherwise most queries will use the wrong query plan.
THis is a classical case where stored procedures are bad. Querying should IMHO not be done using stored procedures at all since modern times - which started about 15 years ago when someone was smart enough to write the first ORM.