SQL: clausola IF all'interno della clausola WHERE


203

È possibile utilizzare una clausola IF all'interno di una clausola WHERE in MS SQL?

Esempio:

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

Risposte:


212

Utilizzare un'istruzione CASE
AGGIORNAMENTO: la sintassi precedente (come sottolineato da alcune persone) non funziona. Puoi usare CASE come segue:

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

Oppure puoi usare un'istruzione IF come sottolinea @ NJ Reed .


[Nota dopo l'aggiornamento dell'autore]: dovrebbe funzionare, ma dovresti TRIM () su entrambi i lati per assicurarti che venga trovata una corrispondenza. Ho la sensazione che ci siano rari casi limite che non corrispondono.
Euro Micelli,

1
L'uso CASEè la soluzione appropriata nella maggior parte dei casi. Nel mio caso, volevo cambiare operatore di confronto e quindi ho usato l'approccio successivo.
Birla,

142

Dovresti essere in grado di farlo senza IF o CASE

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

A seconda del tipo di SQL, potrebbe essere necessario modificare i cast sul numero di ordine su INT o VARCHAR a seconda che siano supportati cast impliciti.

Questa è una tecnica molto comune in una clausola WHERE. Se si desidera applicare una logica "IF" nella clausola WHERE, è sufficiente aggiungere la condizione aggiuntiva con un valore booleano AND alla sezione in cui deve essere applicata.


2
Immagino che tu abbia un po 'un colpo di performance sulla soluzione CASE, sebbene, dato che tutte queste condizioni vengono valutate, no?
Kevin Fairchild,

Dimentico sempre che in SQL si possono sostituire le istruzioni condizionali con una logica booleana del genere. Grazie per il promemoria, è una tecnica molto utile!
CodexArcanum,

1
Questa soluzione è in realtà la migliore a causa del modo in cui il server SQL elabora la logica booleana. Istruzioni CASE in cui le clausole sono meno efficienti dei casi booleani poiché se il primo controllo fallisce, SQL interromperà l'elaborazione della riga e continuerà. Ciò consente di risparmiare tempo di elaborazione. Inoltre, inserisci sempre la dichiarazione più costosa sull'altro lato del tuo controllo booleano.
Steve,

Grazie per una soluzione molto elegante. Ho trovato un tutorial sul metodo che hai usato per aiutare le persone. weblogs.sqlteam.com/jeffs/archive/2003/11/14/513.aspx
Rich

1
@Kash il link che hai fornito è un registro da leggere, c'è qualche documentazione pubblicamente disponibile che descrive ciò che stai dicendo?
Steve

29

Non hai assolutamente bisogno di un'istruzione IF.

WHERE
    (IsNumeric(@OrderNumber) = 1 AND OrderNumber = @OrderNumber)
OR (IsNumeric(@OrderNumber) = 0 AND OrderNumber LIKE '%' + @OrderNumber + '%')

2
Mi piace molto questo approccio. Alternativ utilizza: Filtra solo se AdmUseId ha un valore: where (@AdmUserId is null or CurrentOrder.CustomerAdmUserId = @AdmUserId) O filtra solo se IncludeDeleted = 0: where (@IncludeDeleted = 1 or ItemObject.DeletedFlag = 0)
Kasper Halvas Jensen

Funziona bene quando si utilizza un filtro IN all'interno della clausola WHERE. È difficile fare questo con CASE poiché devi usare COALESCE ed è difficile da leggere, mentre questa è una logica semplice da leggere. Istruzione CASE TSQL nella clausola WHERE per il filtro NOT IN o IN
pholcroft,

14

Non c'è un buon modo per farlo in SQL. Alcuni approcci che ho visto:

1) Usa CASE combinato con operatori booleani:

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) Utilizzare gli IF all'esterno di SELECT

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

3) Utilizzando una stringa lunga, componi l'istruzione SQL in modo condizionale, quindi utilizza EXEC

Il terzo approccio è orribile, ma è quasi l'unico che funzioni se si dispone di una serie di condizioni variabili come questa.


il quarto approccio è quello di convertire tutti i IF...ELSE...condizionali in booleano AND's e OR' s come in @ njr101 risposta di cui sopra. Unico inconveniente di ^ questo approccio è che può essere tremendamente difficile se ne hai molti IFo se ne hai molti nidificati
Don Cheadle,


4

Vuoi l'istruzione CASE

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

3

Penso che dove ... come / = ... il caso ... allora ... possa funzionare con i booleani. Sto usando T-SQL.

Scenario: supponiamo che tu voglia ottenere gli hobby di Person-30 se il bool è falso, e gli hobby di Person-42 se il bool è vero. (Secondo alcuni, le ricerche di hobby comprendono oltre il 90% dei cicli di calcolo aziendale, quindi paga attentamente).

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 OR OrderNumber = @OrderNumber) 
             AND (IsNumber (@OrderNumber) = 1 OR OrderNumber COME '%' 
                                              + @OrderNumber + '%')

Regola di riscrittura della forma normale congiuntiva:IF P THEN Q ELSE R <=> ( ( NOT P ) OR Q ) AND ( P OR R )
onedayquando il

1

L' istruzione CASE è l'opzione migliore rispetto a IF sempre.

  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

In linea caso Condizione funzionerà correttamente.


0

L'esempio seguente esegue una query come parte dell'espressione booleana e quindi esegue blocchi di istruzioni leggermente diversi in base al risultato dell'espressione booleana. Ogni blocco di istruzioni inizia con BEGIN e termina con 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

Uso delle istruzioni IF ... ELSE nidificate L'esempio seguente mostra come un'istruzione IF ... ELSE può essere nidificata all'interno di un'altra. Impostare la variabile @Number su 5, 50 e 500 per testare ciascuna istruzione.

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
Questo non sembra rilevante. Non utilizza un IF (o alcun codice condizionale) in una clausola WHERE.
Vince Bowdren,

0

Nel server sql ho avuto lo stesso problema, volevo usare un'istruzione and solo se il parametro è falso e su true ho dovuto mostrare sia i valori vero che falso, quindi l'ho usato in questo modo

(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 

Vedi se questo aiuta.


-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

Questo non sembra rilevante. Non utilizza un IF (o alcun codice condizionale) in una clausola WHERE.
Vince Bowdren,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.