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

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 .


[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ć.


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

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.


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

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;

2
GDZIE (IsNumeric (@OrderNumber) <> 1 LUB OrderNumber = @OrderNumber) 
             ORAZ (IsNumber (@OrderNumber) = 1 LUB OrderNumber LIKE „%” 
                                              + @OrderNumber + '%')

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 

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

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


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

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)

-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.


-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

To nie wydaje się istotne. Nie używa IF (ani żadnego kodu warunkowego) w klauzuli WHERE.
— Vince Bowdren,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.