views:

170

answers:

4

I have a query:

SELECT ID FROM requests WHERE date <operator> <expression>

I need to change <expression> relying on my stored procedure's parameter.

If flag is set: WHERE date BETWEEN GETDATE() - 7 AND GETDATE()

If is not: WHERE date = date

I tried to use CASE-THEN but it forces using concrete operator but I have to have few:

SELECT ID FROM requests WHERE date = -- here it is
CASE
  WHEN @flag = 1 THEN ..
  ELSE date
END
+3  A: 
select 
    id
from
    requests
where
    (
        flag = 1
        and
        date between getdate()-7 and getdate()
    )
    or
    (
        flag <> 1
        and
        date = date
    )
kurosch
@kurosch: good use of OR. One can exclude the date = date when flag <> 1.
shahkalpesh
This is how one makes queries unsargable...
Remus Rusanu
@Remus: Thats a new buzzword "unsargable". Looked it up on wikipedia. "sargable" = search argument to facilitate use of index. I understand what you mean by "unsargable", now.
shahkalpesh
@Remus: if @flag is 1, the index on the [poorly named] date column (assuming one exists) would be used - no?
OMG Ponies
@rexem: the query optimizer may end up choosing a plan that satisfief *both* conditions `date between...` *and* `date=date` and decide to do a scan. While in this case you *may* get away with the `OR`, the proper thing to do in such situations is always to check the @flag in an ordinary IF and have separate queries on each branch so the optimizer knows exactly what you want in each case.
Remus Rusanu
I'm actually an Oracle person, I know nothing about sqlserver to tsql
kurosch
+1  A: 
SELECT ID FROM requests WHERE 
    (date = date and @flag <> 1 ) 
    or ( date BETWEEN GETDATE() - 7 AND GETDATE() and @flag = 1)

Or in the Transact-SQL

if @flag = 1
    then
     SELECT ID FROM requests WHERE date = date
    else
     SELECT ID FROM requests WHERE date BETWEEN GETDATE() - 7 AND GETDATE()
Mark
+1  A: 

You can simplify it using the good old IF statement

if @flag = 1
   SELECT ID FROM requests WHERE date BETWEEN GETDATE() - 7 AND GETDATE()
else
   SELECT ID FROM requests

EDIT: I am not sure if it works. But, here is what I added in the comment

SELECT ID FROM requests 
WHERE 
CASE WHEN @flag = 1 THEN date BETWEEN GETDATE() - 7 AND GETDATE() 
ELSE 1 = 1 
END

EDIT2: I think it makes sense to use IF statement (if it is in TSQL). Because, the condition relies on external variable other than something from the row

i.e. CASE will do a comparison of @flag for each of the row, which is not needed.

shahkalpesh
The CASE in the WHERE clause won't work, or it fails for me on SQL Server 2005 with 'Incorrect syntax ner the keyword BETWEEN'.
OMG Ponies
@rexem: I do not have SQL server handy to try things. I guess that could be due to DATE as the name of the field?
shahkalpesh
@Shak: No, I was testing with a datetime column called "created". I'm not using 2008, but it's in line with the OPs comments about not getting it to work.
OMG Ponies
The second code scope doesn't work too on my SQL 2008 Express
abatishchev
+1  A: 

As you found out, you can't use a CASE statement in the WHERE clause. Your alternatives are:

Option 1: WHERE clause

SELECT t.id 
  FROM REQUESTS t
 WHERE 1 = 1
    OR (@flag = 1 AND t.date BETWEEN GETDATE() - 7 AND GETDATE())

Option 2: Use IF and have two (or more depending on situation) distinct SQL statements

IF @flag = 1
   SELECT t.id 
     FROM REQUESTS t
ELSE
   SELECT t.id 
     FROM REQUESTS t
    WHERE t.date BETWEEN GETDATE() - 7 AND GETDATE())

Option 3: dynamic SQL (Everyone's favorite)

DECLARE @SQL nvarchar (4000)

SET @SQL = 'SELECT t.id 
              FROM REQUESTS t 
             WHERE 1 = 1'

IF @flag = 1 
  SET @SQL = @SQL + ' AND t.date BETWEEN GETDATE()-7 AND GETDATE()'

EXEC sp_executesql @SQL

Personally, I loath to deal with Option 2. Near identical statements make me want to refactor into a single SQL query. That said, use #1 if things aren't too complex. Otherwise, use dynamic.

OMG Ponies
could you please describe what for are you using 1=1 in option1 ? Unfortunately option2 isn't applicable for my case because original query is much-much bigger then in my OP. I don't trust (yet, maybe) to optimization of dynamic SQL so prefer to use pure t-sql
abatishchev
I had to use the 1 = 1 in Option 1 because no data would return if the flag != 1. It's habit for me to use it in dynamic SQL in case I need to append to the WHERE clause. It's better than using t.date = t.date because that'd use an index for a situation that would always be true.
OMG Ponies