SQL: предложение IF в предложении WHERE

203

Можно ли использовать предложение IF в предложении WHERE в MS SQL?

Пример:

WHERE
    IF IsNumeric(@OrderNumber) = 1
        OrderNumber = @OrderNumber
    ELSE
        OrderNumber LIKE '%' + @OrderNumber + '%'
Брайан Рот
источник

Ответы:

212

Используйте СЛУЧАЙ заявление
UPDATE: Предыдущий синтаксис (как было указано на несколько человек) не работает. Вы можете использовать CASE следующим образом:

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

Или вы можете использовать оператор IF, как указывает @ NJ Reed .

bdukes
источник
[Примечание после ОБНОВЛЕНИЯ автора]: это должно сработать, но вы должны TRIM () с обеих сторон, чтобы убедиться, что совпадение найдено. У меня есть ощущение, что есть редкие крайние случаи, которые не соответствуют.
Евро Мицелли
1
Использование CASEявляется подходящим решением в большинстве случаев. В моем случае я хотел изменить оператор сравнения и, следовательно, я использовал следующий подход.
Бирла
142

Вы должны быть в состоянии сделать это без каких-либо IF или CASE

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

В зависимости от разновидности SQL вам может потребоваться настроить приведение номера заказа к INT или VARCHAR в зависимости от того, поддерживаются ли неявные приведения.

Это очень распространенный метод в предложении WHERE. Если вы хотите применить некоторую логику «ЕСЛИ» в предложении WHERE, все, что вам нужно сделать, это добавить дополнительное условие с логическим И в раздел, где оно должно быть применено.

njr101
источник
2
Я полагаю, что вы немного повлияли на производительность по сравнению с CASE-решением, хотя, так как все эти условия были оценены, не так ли?
Кевин Фэйрчайлд
Я всегда забываю, что в SQL можно заменить условные операторы логической логикой, подобной этой. Спасибо за напоминание, это очень полезная техника!
CodexArcanum
1
Это решение на самом деле является лучшим из-за того, как SQL-сервер обрабатывает логическую логику. Операторы CASE, в которых предложения менее эффективны, чем логические случаи, поскольку, если первая проверка завершится неудачно, SQL прекратит обработку строки и продолжит работу. Это экономит ваше время обработки. Кроме того, всегда помещайте более дорогостоящее заявление на другой стороне вашего логического чека.
Стив
Спасибо за очень элегантное решение. Нашел учебник по методу, который вы использовали, который может помочь людям. weblogs.sqlteam.com/jeffs/archive/2003/11/14/513.aspx
Рич
1
@ Каш ссылка, которую вы предоставили, является регистром для чтения, есть ли общедоступная документация, которая описывает, что вы говорите?
Стив
29

Вам не нужно заявление IF вообще.

WHERE
    (IsNumeric(@OrderNumber) = 1 AND OrderNumber = @OrderNumber)
OR (IsNumeric(@OrderNumber) = 0 AND OrderNumber LIKE '%' + @OrderNumber + '%')
Rivanni
источник
2
Мне очень нравится этот подход. Альтернатива использует: Только фильтр, если AdmUseId имеет значение: where (@AdmUserId is null or CurrentOrder.CustomerAdmUserId = @AdmUserId) Или только фильтр, если IncludeDeleted = 0: where (@IncludeDeleted = 1 or ItemObject.DeletedFlag = 0)
Kasper Halvas Jensen
Это хорошо работает при использовании фильтра IN в предложении WHERE. Делать это с CASE сложно, так как вы должны использовать COALESCE, и его трудно читать, тогда как это простая логика для чтения. Оператор TSQL CASE в предложении WHERE для фильтра NOT IN или IN
pholcroft
14

Нет хорошего способа сделать это в SQL. Некоторые подходы, которые я видел:

1) Используйте CASE в сочетании с логическими операторами:

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) Используйте IF вне SELECT

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

3) Используя длинную строку, составьте свой оператор SQL условно, а затем используйте EXEC

Третий подход отвратителен, но это почти единственная мысль, которая работает, если у вас есть несколько таких переменных условий.

Евро Мицелли
источник
четвертый подход - преобразовать все ваши IF...ELSE...условные выражения в логические ANDи ORкак в ответе @ njr101 выше. Недостатком этого подхода является то, что это может быть чрезвычайно сложно, если у вас их много IF, или если у вас много вложенных
Дон Чидл
6

Используйте оператор CASE вместо IF.

Джоэл Коухорн
источник
4

Вы хотите заявление CASE

WHERE OrderNumber LIKE
CASE WHEN IsNumeric(@OrderNumber)=1 THEN @OrderNumber ELSE '%' + @OrderNumber END
Джефф Мартин
источник
3

Я думаю, что где ... как / = ... case ... тогда ... может работать с логическими значениями. Я использую T-SQL.

Сценарий. Допустим, вы хотите получить увлечения Person-30, если bool имеет значение false, и увлечения Person-42, если bool имеет значение true. (По некоторым данным, хобби-поиски составляют более 90% циклов бизнес-вычислений, так что платите внимательно).

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;
Уильям
источник
2
WHERE (IsNumeric (@OrderNumber) <> 1 ИЛИ OrderNumber = @OrderNumber) 
             AND (IsNumber (@OrderNumber) = 1 ИЛИ OrderNumber LIKE '%' 
                                              + @OrderNumber + '%')
WhoIsNinja
источник
Правило переписывания конъюнктивной Нормальной формы:IF P THEN Q ELSE R <=> ( ( NOT P ) OR Q ) AND ( P OR R )
onedaywhen
1

CASE Statement - лучший вариант, чем IF всегда.

  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 
Маджедур Рахаман
источник
1
    WHERE OrderNumber LIKE CASE WHEN IsNumeric(@OrderNumber) = 1 THEN @OrderNumber ELSE  '%' + @OrderNumber END

В случае строки Условие будет работать правильно.

Джубайер Хоссейн
источник
0

В следующем примере выполняется запрос как часть логического выражения, а затем выполняются несколько другие блоки операторов в зависимости от результата логического выражения. Каждый блок операторов начинается с BEGIN и заканчивается 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

Использование вложенных операторов IF ... ELSE В следующем примере показано, как оператор IF ... ELSE может быть вложен в другой. Установите для переменной @Number значения 5, 50 и 500, чтобы проверить каждый оператор.

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
Хоссейн
источник
2
Это не кажется актуальным. Он не использует IF (или любой условный код) в предложении WHERE.
Винс Боудрен
0

В SQL Server у меня была та же проблема, я хотел использовать оператор and, только если параметр имеет значение false и на true мне пришлось показывать оба значения true и false, поэтому я использовал его таким образом

(T.IsPublic = @ShowPublic or  @ShowPublic = 1)
Анек Азам Хан
источник
-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 

Посмотрите, поможет ли это.

user2164001
источник
-6
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
Хоссейн
источник
Это не кажется актуальным. Он не использует IF (или любой условный код) в предложении WHERE.
Винс Боудрен