SQL: JEŻELI klauzula w klauzuli WHERE

203

Czy możliwe jest użycie klauzuli IF w klauzuli WHERE w MS SQL?

Przykład:

WHERE
    IF IsNumeric(@OrderNumber) = 1
        OrderNumber = @OrderNumber
    ELSE
        OrderNumber LIKE '%' + @OrderNumber + '%'
Bryan Roth
źródło

Odpowiedzi:

212

Użyj instrukcji CASE
UPDATE: Poprzednia składnia (jak wskazało kilka osób) nie działa. Możesz użyć CASE w następujący sposób:

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

Lub możesz użyć wyrażenia IF, takiego jak wskazuje @ NJ Reed .

bdukes
źródło
[Uwaga po aktualizacji przez autora]: To powinno działać, ale powinieneś TRIM () obu stron, aby upewnić się, że znaleziono dopasowanie. Mam przeczucie, że wciąż istnieją rzadkie przypadki, które nie pasują do siebie.
Euro Micelli,
1
Korzystanie CASEjest właściwym rozwiązaniem w większości przypadków. W moim przypadku chciałem zmienić operator porównania i dlatego zastosowałem następne podejście.
Birla,
142

Powinieneś być w stanie to zrobić bez IF ani CASE

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

W zależności od smaku SQL może być konieczne dostosowanie rzutowań numeru zamówienia do INT lub VARCHAR w zależności od tego, czy obsługiwane są rzutowania niejawne.

Jest to bardzo popularna technika w klauzuli WHERE. Jeśli chcesz zastosować logikę „JEŻELI” w klauzuli WHERE, wszystko, co musisz zrobić, to dodać dodatkowy warunek z logiczną wartością logiczną ORAZ do sekcji, w której należy go zastosować.

njr101
źródło
2
Wyobrażam sobie, że bierzesz odrobinę wydajności za rozwiązanie CASE, skoro wszystkie te warunki są oceniane, nie?
Kevin Fairchild,
Zawsze zapominam, że w SQL można zastąpić instrukcje warunkowe taką logiką logiczną. Dzięki za przypomnienie, to bardzo przydatna technika!
CodexArcanum
1
To rozwiązanie jest w rzeczywistości najlepsze ze względu na to, jak SQL Server przetwarza logikę logiczną. Instrukcje CASE, w których klauzule są mniej wydajne niż przypadki boolowskie, ponieważ jeśli pierwsze sprawdzenie zakończy się niepowodzeniem, SQL przestanie przetwarzać linię i będzie kontynuować. To oszczędza czas przetwarzania. Ponadto zawsze umieszczaj droższe zestawienie po drugiej stronie czeku logicznego.
Steve,
Dzięki za bardzo eleganckie rozwiązanie. Znaleziono poradnik na temat zastosowanej metody, który może pomóc ludziom. weblogs.sqlteam.com/jeffs/archive/2003/11/14/513.aspx
Rich
1
@Kash podany przez Ciebie link jest przeznaczony do przeczytania, czy istnieje publicznie dostępna dokumentacja opisująca to, co mówisz?
Steve
29

W ogóle nie potrzebujesz instrukcji IF.

WHERE
    (IsNumeric(@OrderNumber) = 1 AND OrderNumber = @OrderNumber)
OR (IsNumeric(@OrderNumber) = 0 AND OrderNumber LIKE '%' + @OrderNumber + '%')
Rivanni
źródło
2
Naprawdę podoba mi się to podejście. Alternativ używa: Filtruj tylko, jeśli AdmUseId ma wartość: where (@AdmUserId is null or CurrentOrder.CustomerAdmUserId = @AdmUserId) Lub filtruj tylko, jeśli IncludeDeleted = 0: where (@IncludeDeleted = 1 or ItemObject.DeletedFlag = 0)
Kasper Halvas Jensen
Działa to dobrze, gdy używany jest filtr IN w klauzuli WHERE. Robi się to z CASE, ponieważ musisz używać COALESCE i trudno go odczytać, podczas gdy jest to prosta logika do odczytania. Instrukcja TSQL CASE w klauzuli WHERE dla filtra NOT IN lub IN
pholcroft
14

Nie ma dobrego sposobu na zrobienie tego w SQL. Niektóre podejścia, które widziałem:

1) Użyj CASE w połączeniu z operatorami logicznymi:

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) Użyj JEŻELI poza WYBOREM

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

3) Używając długiego łańcucha, ułóż warunkowo instrukcję SQL, a następnie użyj EXEC

Trzecie podejście jest ohydne, ale jest to prawie jedyna myśl, która działa, jeśli masz wiele takich zmiennych warunków.

Euro Micelli
źródło
czwarte podejście polega na przekształceniu wszystkich IF...ELSE...warunkowych w logiczne ANDi logiczne , ORjak w powyższej odpowiedzi @ njr101. Wadą tego podejścia jest to, że może być niezwykle trudne, jeśli masz wiele IFlub wiele z nich jest zagnieżdżonych
Don Cheadle
4

Chcesz instrukcji CASE

WHERE OrderNumber LIKE
CASE WHEN IsNumeric(@OrderNumber)=1 THEN @OrderNumber ELSE '%' + @OrderNumber END
Jeff Martin
źródło
3

Myślę, że gdzie ... jak / = ... przypadek ... to ... może współpracować z Booleanami. Używam T-SQL.

Scenariusz: Powiedzmy, że chcesz zdobyć hobby Osoby-30, jeśli bool jest fałszywy, i hobby Osoby-42, jeśli bool jest prawdziwy. (Według niektórych wyszukiwanie hobbystyczne obejmuje ponad 90% cykli obliczeniowych w biznesie, więc zwróć uwagę.)

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
źródło
2
GDZIE (IsNumeric (@OrderNumber) <> 1 LUB OrderNumber = @OrderNumber) 
             ORAZ (IsNumber (@OrderNumber) = 1 LUB OrderNumber LIKE „%” 
                                              + @OrderNumber + '%')
WhoIsNinja
źródło
Reguła przepisywania koniunkcyjna postać normalna:IF P THEN Q ELSE R <=> ( ( NOT P ) OR Q ) AND ( P OR R )
onedaywhen
1

Instrukcja CASE jest lepszą opcją niż JEŚLI zawsze.

  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 Rahaman
źródło
1
    WHERE OrderNumber LIKE CASE WHEN IsNumeric(@OrderNumber) = 1 THEN @OrderNumber ELSE  '%' + @OrderNumber END

W przypadku linii Warunek będzie działał poprawnie.

Jubayer Hossain
źródło
0

Poniższy przykład wykonuje kwerendę jako część wyrażenia logicznego, a następnie wykonuje nieco inne bloki instrukcji w zależności od wyniku wyrażenia logicznego. Każdy blok instrukcji zaczyna się od BEGIN i kończy się 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

Korzystanie z zagnieżdżonych instrukcji IF ... ELSE Poniższy przykład pokazuje, w jaki sposób można zagnieździć instrukcję IF… ELSE wewnątrz innej instrukcji. Ustaw zmienną @Number na 5, 50 i 500, aby przetestować każdą instrukcję.

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
hossein
źródło
2
To nie wydaje się istotne. Nie używa IF (ani żadnego kodu warunkowego) w klauzuli WHERE.
Vince Bowdren,
0

Na serwerze sql miałem ten sam problem. Chciałem użyć instrukcji i tylko wtedy, gdy parametr ma wartość false, a na true musiałem pokazywać obie wartości true i false, więc użyłem go w ten sposób

(T.IsPublic = @ShowPublic or  @ShowPublic = 1)
Aneeq Azam Khan
źródło
-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 

Sprawdź, czy to pomoże.

użytkownik2164001
źródło
-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
hossein
źródło
To nie wydaje się istotne. Nie używa IF (ani żadnego kodu warunkowego) w klauzuli WHERE.
Vince Bowdren,