Reputation: 1071
I am trying to change the conditions of PART of a where based on some value. I have looked at similar articles on here including:
SQL Switch/Case in where clause
But they aren't quite dealing with my case.
What I have is a stored procedure that does a select based on its inputs. Those inputs are optional (or have default parameters as they prefer to call it).
2 of said parameters are numbers.
This is what I tried (which obviously didn't work)
DECLARE @SECOND INT;
DECLARE @FIRST INT;
SET @FIRST = 123456;
SET @SECOND = 67890;
SELECT * FROM BANK_DETAIL
WHERE -- POSSIBLY SOME OTHER WHERE CLAUSES
CASE
WHEN @SECOND IS NULL THEN
X = @FIRST
ELSE
X >= @FIRST AND X <= @SECOND
END
-- POSSIBLY SOME MORE WHERE CLAUSES
ORDER BY X
REALLY this feels like it needs an IF/ELSE rather than a CASE but I was directed towards CASE.....
Oh, this is MS SQL >= 2005
Upvotes: 4
Views: 3505
Reputation: 294407
You're building a dynamic search condition. By forcing one single statement to cover both cases you are cutting the optimizer options. The generated plan has to work in both cases when @seconds
is null and when is not null. You'll be much better using two separate statement:
IF @SECOND IS NULL THEN
SELECT * FROM BANK_DETAIL
WHERE X = @FIRST
ORDER BY X
ELSE
SELECT * FROM BANK_DETAIL
WHERE X >= @FIRST AND X <= @SECOND
ORDER BY X
You intuition to 'simplify' into one single statement is leading you down the wrong path. The result is less text, but much more execution time due to suboptimal plans. The article linked at the beginning of my response goes into great detail on this topic.
Upvotes: 1
Reputation: 47392
The problem is that CASE
returns a value, it's not a branch in logic. The link that OMG provides is pretty much the authoritative source on this one (almost anything from Erland Sommarskog is going to be great advice).
A quick summary of the link:
You can use dynamic SQL where you build up a statement based on the conditions. This can often be the best performing approach, but there are drawbacks. One of the biggest drawbacks is the possible security issues, so make sure that you fully understand SQL injection attacks and how to prevent them.
Another approach is to use complex logic in your WHERE statement using ORs. In your case it would be something like below. This approach is a bit simpler than dynamic SQL and safer, but performance may not always be great. If performance is ok for your situation though (test it) then stick with this approach.
SELECT
* -- I assume that you used * just as an example and don't actually use this in your production code
FROM
Bank_Detail
WHERE -- POSSIBLY SOME OTHER WHERE CLAUSES
(@second IS NULL AND X = @first) OR
(@second IS NOT NULL AND (x >= @first AND x <= @second))
-- POSSIBLY SOME MORE WHERE CLAUSES
ORDER BY
x
Another way to organize the statement just occurred to me...
SELECT
*
FROM
Bank_Detail
WHERE -- POSSIBLY SOME OTHER WHERE CLAUSES
x >= @first AND
x <= COALESCE(@second, @first)
-- POSSIBLY SOME MORE WHERE CLAUSES
ORDER BY
x
I haven't tested it yet, but I think that will be logically equivalent and may give you a more consistent query plan.
Erland also gives a couple other possible approaches, so be sure to read his full article on the subject.
Upvotes: 0
Reputation: 174427
I would use boolean operators:
SELECT * FROM BANK_DETAIL
WHERE
((@SECOND IS NULL AND X = @FIRST) OR
(@SECOND IS NOT NULL AND X >= @FIRST AND X <= @SECOND));
Upvotes: 2
Reputation: 2684
Try conditioning on the nullity of @SECOND instead of using CASE.
SELECT *
FROM BANK_DETAIL
WHERE
-- other conditions go here
AND ((@SECOND IS NULL AND X = @FIRST)
OR (@SECOND IS NOT NULL AND X >= @FIRST AND X <= @SECOND))
Upvotes: 4
Reputation: 22858
SELECT * FROM BANK_DETAIL
WHERE
(@SECOND IS NULL AND X = @FIRST) or
(X >= @FIRST AND X <= @SECOND)
although it would be more efficient to do:
IF @SECOND IS NULL
BEGIN
SELECT * FROM BANK_DETAIL
WHERE
X = @FIRST
END
ELSE
BEGIN
SELECT * FROM BANK_DETAIL
WHERE
X >= @FIRST AND X <= @SECOND
END
Upvotes: 3