SQL: cláusula IF dentro de la cláusula WHERE

203

¿Es posible usar una cláusula IF dentro de una cláusula WHERE en MS SQL?

Ejemplo:

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

Respuestas:

212

Utilice una instrucción CASE
ACTUALIZACIÓN: la sintaxis anterior (como lo señalaron algunas personas) no funciona. Puede usar CASE de la siguiente manera:

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

O puede usar una declaración IF como @ NJ Reed señala.

bdukes
fuente
[Nota después de la ACTUALIZACIÓN por autor]: Eso debería funcionar, pero debe RECORTAR () a ambos lados para asegurarse de que se encuentre una coincidencia. Tengo el presentimiento de que todavía hay casos raros que aún no coinciden.
Euro Micelli
1
Usar CASEes la solución adecuada en la mayoría de los casos. En mi caso, quería cambiar el operador de comparación y, por lo tanto, utilicé el siguiente enfoque.
Birla
142

Debería poder hacer esto sin IF o CASE

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

Dependiendo del sabor de SQL, es posible que necesite ajustar los conversiones en el número de pedido a un INT o VARCHAR dependiendo de si se admiten conversiones implícitas.

Esta es una técnica muy común en una cláusula WHERE. Si desea aplicar alguna lógica "IF" en la cláusula WHERE, todo lo que necesita hacer es agregar la condición adicional con un booleano AND a la sección donde debe aplicarse.

njr101
fuente
2
Sin embargo, me imagino que tiene un poco de éxito en el rendimiento sobre la solución CASE, ya que todas esas condiciones se evalúan, ¿no?
Kevin Fairchild
Siempre olvido que en SQL uno puede reemplazar las declaraciones condicionales con una lógica booleana como esa. Gracias por el recordatorio, ¡es una técnica muy útil!
CodexArcanum
1
Esta solución es en realidad la mejor debido a cómo el servidor SQL procesa la lógica booleana. Declaraciones de CASO en donde las cláusulas son menos eficientes que los casos booleanos ya que si la primera verificación falla, SQL detendrá el procesamiento de la línea y continuará. Eso le ahorra tiempo de procesamiento. Además, siempre coloque la declaración más costosa al otro lado de su cheque booleano.
Steve
Gracias por una solución muy elegante. Encontré un tutorial sobre el método que usó que puede ayudar a las personas. weblogs.sqlteam.com/jeffs/archive/2003/11/14/513.aspx
Rico el
1
@Kash el enlace que proporcionó es un registro para leer, ¿hay alguna documentación disponible públicamente que describa lo que está diciendo?
Steve
29

No necesita una declaración IF en absoluto.

WHERE
    (IsNumeric(@OrderNumber) = 1 AND OrderNumber = @OrderNumber)
OR (IsNumeric(@OrderNumber) = 0 AND OrderNumber LIKE '%' + @OrderNumber + '%')
Rivanni
fuente
2
Realmente me gusta este enfoque. Usos alternativos: solo filtra si AdmUseId tiene un valor: where (@AdmUserId is null or CurrentOrder.CustomerAdmUserId = @AdmUserId) o solo filtra si IncludeDeleted = 0: where (@IncludeDeleted = 1 or ItemObject.DeletedFlag = 0)
Kasper Halvas Jensen el
Esto funciona bien cuando se usa un filtro IN dentro de la cláusula WHERE. Hacer esto es complicado con CASE ya que tienes que usar COALESCE y es difícil de leer, mientras que esta es una lógica sencilla de leer. Declaración de CASO TSQL en la cláusula WHERE para el filtro NOT IN o IN
pholcroft
14

No hay una buena manera de hacer esto en SQL. Algunos enfoques que he visto:

1) Use CASE combinado con operadores booleanos:

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) Use IF fuera de SELECT

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

3) Usando una cadena larga, componga su declaración SQL condicionalmente, y luego use EXEC

El tercer enfoque es horrible, pero es casi el único que funciona si tiene una serie de condiciones variables como esa.

Euro Micelli
fuente
el cuarto enfoque consiste en convertir todos sus IF...ELSE...condicionales en booleano AND's y OR' s como en @ njr101 respuesta anterior. La desventaja de ^ este enfoque es que puede ser muy difícil si tienes muchos IF, o si tienes muchos anidados
Don Cheadle
6

Use una declaración CASE en lugar de IF.

Joel Coehoorn
fuente
4

Desea la declaración CASE

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

Creo que donde ... como / = ... case ... entonces ... puede funcionar con Booleans. Estoy usando T-SQL.

Escenario: Digamos que desea obtener los pasatiempos de la Persona-30 si bool es falso y los pasatiempos de la Persona-42 si bool es verdadero. (Según algunos, las búsquedas de pasatiempos comprenden más del 90% de los ciclos de cómputo de negocios, por lo tanto, pague cerca).

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;
Guillermo
fuente
2
WHERE (IsNumeric (@OrderNumber) <> 1 OR OrderNumber = @OrderNumber) 
             AND (IsNumber (@OrderNumber) = 1 OR OrderNumber LIKE '%' 
                                              + @OrderNumber + '%')
WhoIsNinja
fuente
Regla de reescritura de forma normal conjuntiva:IF P THEN Q ELSE R <=> ( ( NOT P ) OR Q ) AND ( P OR R )
cuando el
1

La declaración CASE es una mejor opción que SI siempre.

  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
fuente
1
    WHERE OrderNumber LIKE CASE WHEN IsNumeric(@OrderNumber) = 1 THEN @OrderNumber ELSE  '%' + @OrderNumber END

En línea caso Condición funcionará correctamente.

Jubayer Hossain
fuente
0

El siguiente ejemplo ejecuta una consulta como parte de la expresión booleana y luego ejecuta bloques de sentencias ligeramente diferentes en función del resultado de la expresión booleana. Cada bloque de instrucciones comienza con BEGIN y 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 de instrucciones anidadas IF ... ELSE El siguiente ejemplo muestra cómo una declaración IF ... ELSE puede anidarse dentro de otra. Establezca la variable @Number en 5, 50 y 500 para probar cada instrucción.

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
fuente
2
Esto no parece relevante. No usa un IF (o cualquier código condicional) en una cláusula WHERE.
Vince Bowdren
0

En el servidor sql tuve el mismo problema, quería usar una instrucción y solo si el parámetro es falso y en verdadero tuve que mostrar los valores verdadero y falso, así que lo usé de esta manera

(T.IsPublic = @ShowPublic or  @ShowPublic = 1)
Aneeq Azam Khan
fuente
-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 

A ver si esto ayuda.

usuario2164001
fuente
-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
fuente
Esto no parece relevante. No usa un IF (o cualquier código condicional) en una cláusula WHERE.
Vince Bowdren