246

Is it possible to use an IF clause within a WHERE clause in MS SQL?

Example:

WHERE
    IF IsNumeric(@OrderNumber) = 1
        OrderNumber = @OrderNumber
    ELSE
        OrderNumber LIKE '%' + @OrderNumber + '%'
Bryan Roth
  • 10,479
  • 15
  • 47
  • 56

16 Answers16

263

Use a CASE statement
UPDATE: The previous syntax (as pointed out by a few people) doesn't work. You can use CASE as follows:

WHERE OrderNumber LIKE
  CASE WHEN IsNumeric(@OrderNumber) = 1 THEN 
    @OrderNumber 
  ELSE
    '%' + @OrderNumber
  END

Or you can use an IF statement like @N. J. Reed points out.

Community
  • 1
  • 1
bdukes
  • 152,002
  • 23
  • 148
  • 175
  • [Note after the UPDATE by author]: That should work, but you should TRIM() both sides to make sure that a match is found. I have a gut feeling that there rare still edge cases that fail to match. – Euro Micelli Sep 17 '08 at 21:51
  • 1
    Using `CASE` is the appropriate solution in most cases. In my case, I wanted the change the comparison operator and hence I used the next approach. – Birla Nov 15 '14 at 11:59
  • 4
    How can we use this with IN operator? – DSR Jul 24 '20 at 14:11
161

You should be able to do this without any IF or CASE

 WHERE 
   (IsNumeric(@OrderNumber) AND
      (CAST OrderNumber AS VARCHAR) = (CAST @OrderNumber AS VARCHAR)
 OR
   (NOT IsNumeric(@OrderNumber) AND
       OrderNumber LIKE ('%' + @OrderNumber))

Depending on the flavour of SQL you may need to tweak the casts on the order number to an INT or VARCHAR depending on whether implicit casts are supported.

This is a very common technique in a WHERE clause. If you want to apply some "IF" logic in the WHERE clause all you need to do is add the extra condition with an boolean AND to the section where it needs to be applied.

njr101
  • 9,499
  • 7
  • 39
  • 56
  • 2
    I'd imagine you take a bit of a performance hit over the CASE solution, though, since all of those conditions get evaluated, no? – Kevin Fairchild Sep 26 '08 at 13:06
  • 2
    I always forget that in SQL one can replace conditional statements with boolean logic like that. Thanks for the reminder, it's a very useful technique! – CodexArcanum Oct 08 '10 at 16:05
  • 2
    This solution is actually the best one due to how SQL server processes the boolean logic. CASE statements in where clauses are less efficient than boolean cases since if the first check fails, SQL will stop processing the line and continue on. That saves you processing time. Also, always put the more costly statement on the other side of your boolean check. – Steve Dec 17 '10 at 19:12
  • 1
    Thanks for a very elegant solution . Found a tutorial on the method you used which may help people. http://weblogs.sqlteam.com/jeffs/archive/2003/11/14/513.aspx – Rich Sep 06 '11 at 14:16
  • @Steve, that is not necessarily true. First of all, the query optimizer decides which side of the AND/OR to evaluate first using an optimization technique called contradiction detection. But in this case, as you are using @ OrderNumber variable, both of them would get evaluated and this will not "short circuit" like you described. More info on short-circuiting here: [Understanding T-SQL Expression Short-Circuiting](http://www.sqlservercentral.com/articles/T-SQL/71950/) – Kash Jul 27 '12 at 19:37
  • 1
    @Kash the link you provided is a register-to-read, is there any publicly available documenation that describes what you are saying? – Steve Mar 14 '13 at 19:26
  • this was the key technique I used when migrating a stored proc from using a big SQL string, dependent on things like `IF (..) SET @sqlSTR = @sqlSTR + ' and u.user = ...'`, to doing it all in regular SQL – Don Cheadle Jul 08 '15 at 21:42
45

You don't need a IF statement at all.

WHERE
    (IsNumeric(@OrderNumber) = 1 AND OrderNumber = @OrderNumber)
OR (IsNumeric(@OrderNumber) = 0 AND OrderNumber LIKE '%' + @OrderNumber + '%')
Rivanni
  • 649
  • 6
  • 4
  • 2
    I really like this approach. Alternativ uses: Only filter if AdmUseId has a value: `where (@AdmUserId is null or CurrentOrder.CustomerAdmUserId = @AdmUserId)` Or only filter if IncludeDeleted = 0: `where (@IncludeDeleted = 1 or ItemObject.DeletedFlag = 0)` – Kasper Jensen Nov 08 '17 at 08:22
  • This works well when using an IN filter within the WHERE clause. It gets messing doing this with CASE as you have to use COALESCE and it is hard to read, whereas this is straightforward logic to read. [TSQL CASE statement in WHERE clause for NOT IN or IN filter](https://shazwazza.com/post/tsql-case-statement-in-where-clause-for-not-in-or-in-filter/) – pholcroft Apr 07 '20 at 15:56
  • 1
    This is exactly what I was looking for, thanks so much for the concise example (some 5 years later, heh). – wnjl Oct 03 '21 at 22:58
  • Perfect! Thanks! – Carthax Oct 14 '21 at 19:25
18

There isn't a good way to do this in SQL. Some approaches I have seen:

1) Use CASE combined with boolean operators:

WHERE
    OrderNumber = CASE 
        WHEN (IsNumeric(@OrderNumber) = 1)
        THEN CONVERT(INT, @OrderNumber)
        ELSE -9999 -- Some numeric value that just cannot exist in the column
    END
    OR 
    FirstName LIKE CASE
        WHEN (IsNumeric(@OrderNumber) = 0)
        THEN '%' + @OrderNumber
        ELSE ''
    END

2) Use IF's outside the SELECT

IF (IsNumeric(@OrderNumber)) = 1
BEGIN
    SELECT * FROM Table
    WHERE @OrderNumber = OrderNumber
END ELSE BEGIN
    SELECT * FROM Table
    WHERE OrderNumber LIKE '%' + @OrderNumber
END

3) Using a long string, compose your SQL statement conditionally, and then use EXEC

The 3rd approach is hideous, but it's almost the only think that works if you have a number of variable conditions like that.

Euro Micelli
  • 33,285
  • 8
  • 51
  • 70
  • the fourth approach is to convert all your `IF...ELSE...` conditionals into boolean `AND`'s and `OR`'s as in @njr101 answer above. Downside to ^ this approach is that it can be brain-fryingly difficult if you have many `IF`'s, or if you have many that are nested – Don Cheadle Jul 09 '15 at 18:08
6

To clarify some of the logical equivalence solutions.

An if statement

if (a) then b

is logically equivalent to

(!a || b)

It's the first line on the Logical equivalences involving conditional statements section of the Logical equivalence wikipedia article.

To include the else, all you would do is add another conditional

if(a) then b; 
if(!a) then c;

which is logically equivalent to (!a || b) && (a || c)

So using the OP as an example:

IF IsNumeric(@OrderNumber) = 1
    OrderNumber = @OrderNumber
ELSE
    OrderNumber LIKE '%' + @OrderNumber + '%'

the logical equivalent would be:

(IsNumeric(@OrderNumber) <> 1 OR OrderNumber = @OrderNumber)
AND (IsNumeric(@OrderNumber) = 1 OR OrderNumber LIKE '%' + @OrderNumber + '%' )
Basic.Bear
  • 111
  • 1
  • 9
  • thank you for the logical equivalence reference. The other answers seem to not do this right – netotz Jun 21 '23 at 19:09
6

Use a CASE statement instead of IF.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
4

You want the CASE statement

WHERE OrderNumber LIKE
CASE WHEN IsNumeric(@OrderNumber)=1 THEN @OrderNumber ELSE '%' + @OrderNumber END
sth
  • 222,467
  • 53
  • 283
  • 367
Jeff Martin
  • 10,812
  • 7
  • 48
  • 74
3

I think that where...like/=...case...then... can work with Booleans. I am using T-SQL.

Scenario: Let's say you want to get Person-30's hobbies if bool is false, and Person-42's hobbies if bool is true. (According to some, hobby-lookups comprise over 90% of business computation cycles, so pay close attn.).

CREATE PROCEDURE sp_Case
@bool   bit
AS
SELECT Person.Hobbies
FROM Person
WHERE Person.ID = 
    case @bool 
        when 0 
            then 30
        when 1
            then 42
    end;
William
  • 31
  • 1
3

// an example for using a stored procedure to select users filtered by country and site

CREATE STORED PROCEDURE GetUsers
@CountryId int = null,
@SiteId int = null
AS
BEGIN
SELECT *
        FROM Users
        WHERE
                CountryId  = CASE WHEN ISNUMERIC(@CountryId) = 1 THEN @CountryId ELSE CountryId END AND 
                SiteId     = CASE WHEN ISNUMERIC(@SiteId) = 1 THEN @SiteId ELSE SiteId END END

// take from the input countryId AND/OR siteId if exists else don't filter

  • Welcome to StackOverflow. While this code may solve the question, [including an explanation](https://meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit](https://stackoverflow.com/posts/65439563/edit) your answer to add explanations and give an indication of what limitations and assumptions apply. – Ruli Dec 24 '20 at 14:46
2
WHERE (IsNumeric(@OrderNumber) <> 1 OR OrderNumber = @OrderNumber) 
             AND (IsNumber(@OrderNumber) = 1 OR OrderNumber LIKE '%' 
                                              + @OrderNumber + '%')
ns12345
  • 3,079
  • 5
  • 41
  • 62
1

CASE Statement is better option than IF always.

  WHERE  vfl.CreatedDate >= CASE WHEN @FromDate IS NULL THEN vfl.CreatedDate ELSE  @FromDate END
    AND vfl.CreatedDate<=CASE WHEN @ToDate IS NULL THEN vfl.CreatedDate ELSE @ToDate END 
Majedur
  • 3,074
  • 1
  • 30
  • 43
1
    WHERE OrderNumber LIKE CASE WHEN IsNumeric(@OrderNumber) = 1 THEN @OrderNumber ELSE  '%' + @OrderNumber END

In line case Condition will work properly.

1

In sql server I had same problem I wanted to use an and statement only if parameter is false and on true I had to show both values true and false so I used it this way

(T.IsPublic = @ShowPublic or  @ShowPublic = 1)
Aneeq Azam Khan
  • 992
  • 1
  • 10
  • 23
0

The following example executes a query as part of the Boolean expression and then executes slightly different statement blocks based on the result of the Boolean expression. Each statement block starts with BEGIN and completes with END.

USE AdventureWorks2012;
GO
DECLARE @AvgWeight decimal(8,2), @BikeCount int
IF 
(SELECT COUNT(*) FROM Production.Product WHERE Name LIKE 'Touring-3000%' ) > 5
BEGIN
   SET @BikeCount = 
        (SELECT COUNT(*) 
         FROM Production.Product 
         WHERE Name LIKE 'Touring-3000%');
   SET @AvgWeight = 
        (SELECT AVG(Weight) 
         FROM Production.Product 
         WHERE Name LIKE 'Touring-3000%');
   PRINT 'There are ' + CAST(@BikeCount AS varchar(3)) + ' Touring-3000 bikes.'
   PRINT 'The average weight of the top 5 Touring-3000 bikes is ' + CAST(@AvgWeight AS varchar(8)) + '.';
END
ELSE 
BEGIN
SET @AvgWeight = 
        (SELECT AVG(Weight)
         FROM Production.Product 
         WHERE Name LIKE 'Touring-3000%' );
   PRINT 'Average weight of the Touring-3000 bikes is ' + CAST(@AvgWeight AS varchar(8)) + '.' ;
END ;
GO

Using nested IF...ELSE statements The following example shows how an IF … ELSE statement can be nested inside another. Set the @Number variable to 5, 50, and 500 to test each statement.

DECLARE @Number int
SET @Number = 50
IF @Number > 100
   PRINT 'The number is large.'
ELSE 
   BEGIN
      IF @Number < 10
      PRINT 'The number is small'
   ELSE
      PRINT 'The number is medium'
   END ;
GO
McDowell
  • 107,573
  • 31
  • 204
  • 267
-1
If @LstTransDt is Null
                begin
                    Set @OpenQty=0
                end
            else
                begin
                   Select   @OpenQty=IsNull(Sum(ClosingQty),0)  
                   From  ProductAndDepotWiseMonitoring  
                   Where   Pcd=@PCd And PtpCd=@PTpCd And TransDt=@LstTransDt      
                end 

See if this helps.

NoNaMe
  • 6,020
  • 30
  • 82
  • 110
-8
USE AdventureWorks2012;
GO
IF 
(SELECT COUNT(*) FROM Production.Product WHERE Name LIKE 'Touring-3000%' ) > 5
PRINT 'There are more than 5 Touring-3000 bicycles.'
ELSE PRINT 'There are 5 or less Touring-3000 bicycles.' ;
GO
kleopatra
  • 51,061
  • 28
  • 99
  • 211