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 .


[Примечание после ОБНОВЛЕНИЯ автора]: это должно сработать, но вы должны 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, все, что вам нужно сделать, это добавить дополнительное условие с логическим И в раздел, где оно должно быть применено.


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 + '%')

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, или если у вас много вложенных
— Дон Чидл



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 + '%')

Правило переписывания конъюнктивной Нормальной формы: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 

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


-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.
— Винс Боудрен
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.