Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CASE statement in where clause in tsql query

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)
like image 479
Barzul Avatar asked Sep 16 '14 15:09

Barzul


People also ask

Can we use CASE statement in where clause in SQL?

You can use a CASE Statement anywhere a valid expression is used within the SELECT statement such as the WHERE clause's filter criteria.

Which is a case where the SQL?

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.

How do you write a CASE statement in SQL?

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.

Where should you insert a CASE statement in a query?

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.


2 Answers

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 casestatement. Both conditions have the same value for @type. I assume that is a typo.

like image 183
Gordon Linoff Avatar answered Nov 03 '22 06:11

Gordon Linoff


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.

like image 38
GarethD Avatar answered Nov 03 '22 06:11

GarethD