I'm trying to write a case statement in the where clause for a query I am working on. I am importing the code in Crystal reports and I am basically trying to say if the variable 'type' is set to 'create' run for this date range in the where clause else run for a different date range. It keeps giving me an error. I can't seem to identify what is wrong with my syntax here. Help anyone?
DECLARE @Date1 DATETIME
DECLARE @Date2 DATETIME
DECLARE @type VARCHAR(20)
SET @Date1 = '2010-1-1'
SET @Date2 = '2010-2-1'
SET @type = '{?DateType}'
select *
from filled
WHERE
(CASE WHEN @type = 'create' THEN
filled.CREATEDON >= @Date1
AND filled.CREATEDON < DATEADD(d, +1, @Date2)
WHEN @type <> 'create' THEN
filled.datefilled >= @Date1
AND filled.datefilled < DATEADD(d, +1, @Date2)
END)
You can use a CASE Statement anywhere a valid expression is used within the SELECT statement such as the WHERE clause's filter criteria.
The SQL CASE ExpressionThe CASE expression goes through conditions and returns a value when the first condition is met (like an if-then-else statement). So, once a condition is true, it will stop reading and return the result. If no conditions are true, it returns the value in the ELSE clause.
SQL Case Statement Syntax Then for a single condition you can write the keyword WHEN followed by the condition that has to be satisfied. After that comes the keyword THEN and the value for that condition, like WHEN <condition> THEN <stuff> . This can then be followed by other WHEN / THEN statements.
The CASE statement has to be included inside the SELECT Statement. It starts with the CASE keyword followed by the WHEN keyword and then the CONDITION. The condition can be any valid SQL Server expression which returns a boolean value.
You don't need a case
statement
WHERE ( (@type = 'create' and filled.CREATEDON >= @Date1 AND filled.CREATEDON < DATEADD(d, +1, @Date2) ) or
(@type <> 'create' and filled.datefilled >= @Date1 AND filled.datefilled < DATEADD(d, +1, @Date2) )
)
This leaves the non-sensical logic you have in the case
statement. Both conditions have the same value for @type
. I assume that is a typo.
Using a CASE expression in a where clause is possible, but generally it can be avoided, and rewritten using AND/OR
, IN your case it would be:
WHERE( @Type = 'create'
AND filled.CREATEDON >= @Date1
AND filled.CREATEDON < DATEADD(d, +1, @Date2)
)
OR ( @Type != 'create'
AND filled.datefilled >= @Date1
AND filled.datefilled < DATEADD(d, +1, @Date2)
)
HOWEVER queries like this usually produce suboptimal plans. You should use IF/ELSE
logic if possible:
IF @Type = 'create'
BEGIN
SELECT *
FROM Filled
WHERE Filled.CreatedOn >= @Date1
AND Filled.CreatedOn < DATEADD(DAY, 1, @Date2)
END
ELSE
BEGIN
SELECT *
FROM Filled
WHERE Filled.DateFilled >= @Date1
AND Filled.DateFilled < DATEADD(DAY, 1, @Date2)
END
The reason for this is the value of @type is not known at compile time, therefore the optimiser does not know whether it will need to search on DateFilled
or CreatedOn
, therefore cannot plan to use an index on either column (if one exists), so will do a table scan regardless of the indexes available. Whereas if you separate the logic with IF/ELSE
it does not matter what the value of @type is, a plan is created for each branch of the IF
, and in each branch the optimiser knows which column will be searched, and can plan to use the appropriate index.
You can also use UNION ALL
:
SELECT *
FROM Filled
WHERE Filled.CreatedOn >= @Date1
AND Filled.CreatedOn < DATEADD(DAY, 1, @Date2)
AND @Type = 'create'
UNION ALL
SELECT *
FROM Filled
WHERE Filled.DateFilled >= @Date1
AND Filled.DateFilled < DATEADD(DAY, 1, @Date2)
AND @Type <> 'create';
Again, if indexes exist on DateFilled
or CreatedOn
this is much more likely to produce a plan that uses them than using OR
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With