Cláusula SQL: IF na cláusula WHERE

203

É possível usar uma cláusula IF dentro de uma cláusula WHERE no MS SQL?

Exemplo:

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

Respostas:

212

Use uma instrução CASE
UPDATE: A sintaxe anterior (conforme apontado por algumas pessoas) não funciona. Você pode usar o CASE da seguinte maneira:

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

Ou você pode usar uma declaração IF como @ NJ Reed aponta.

bdukes
fonte
[Observe após a atualização do autor]: Isso deve funcionar, mas você deve TRIM () de ambos os lados para garantir que uma correspondência seja encontrada. Tenho a sensação de que existem casos raros que ainda não coincidem.
Euro Micelli 17/09/08
1
Usar CASEé a solução apropriada na maioria dos casos. No meu caso, eu queria mudar o operador de comparação e, portanto, usei a próxima abordagem.
Birla
142

Você deve conseguir fazer isso sem nenhum IF ou CASE

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

Dependendo do tipo de SQL, pode ser necessário ajustar as transmissões no número do pedido para um INT ou VARCHAR, dependendo do suporte a transmissões implícitas.

Essa é uma técnica muito comum em uma cláusula WHERE. Se você deseja aplicar alguma lógica "SE" na cláusula WHERE, tudo o que você precisa fazer é adicionar a condição extra com um AND booleano à seção em que ela precisa ser aplicada.

njr101
fonte
2
Imagino que você sofra um pouco de desempenho com a solução CASE, já que todas essas condições são avaliadas, não?
22408 Kevin Fairchild
Eu sempre esqueço que no SQL é possível substituir instruções condicionais por uma lógica booleana como essa. Obrigado pelo lembrete, é uma técnica muito útil!
CodexArcanum
1
Esta solução é realmente a melhor devido a como o SQL Server processa a lógica booleana. Instruções CASE nas cláusulas where são menos eficientes do que casos booleanos, pois se a primeira verificação falhar, o SQL interromperá o processamento da linha e continuará. Isso economiza tempo de processamento. Além disso, sempre coloque a declaração mais cara do outro lado da sua verificação booleana.
21710 Steve Steve
Obrigado por uma solução muito elegante. Encontrei um tutorial sobre o método usado, que pode ajudar as pessoas. weblogs.sqlteam.com/jeffs/archive/2003/11/14/513.aspx
Rich
1
@Kash O link que você forneceu é um registro para leitura. Existe alguma documentação disponível ao público que descreva o que você está dizendo?
Steve
29

Você não precisa de uma declaração SE.

WHERE
    (IsNumeric(@OrderNumber) = 1 AND OrderNumber = @OrderNumber)
OR (IsNumeric(@OrderNumber) = 0 AND OrderNumber LIKE '%' + @OrderNumber + '%')
Rivanni
fonte
2
Eu realmente gosto dessa abordagem. Usos alternativ: único filtro se AdmUseId tem um valor: where (@AdmUserId is null or CurrentOrder.CustomerAdmUserId = @AdmUserId) Ou único filtro se IncludeDeleted = 0: where (@IncludeDeleted = 1 or ItemObject.DeletedFlag = 0)
Kasper Jensen halvas
Isso funciona bem ao usar um filtro IN na cláusula WHERE. Fica bagunçado ao fazer isso com CASE, pois você precisa usar COALESCE e é difícil de ler, enquanto essa é uma lógica direta de se ler. Instrução TSQL CASE na cláusula WHERE para filtro NOT IN ou IN
pholcroft
14

Não há uma boa maneira de fazer isso no SQL. Algumas abordagens que eu já vi:

1) Use CASE combinado com 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 IFs fora do 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 uma cadeia longa, componha sua instrução SQL condicionalmente e use EXEC

A terceira abordagem é hedionda, mas é quase a única coisa que funciona se você tiver várias condições variáveis ​​como essa.

Euro Micelli
fonte
a quarta abordagem é converter todos os seus IF...ELSE...condicionais em booleanos ANDe ORs, como na resposta @ njr101 acima. Desvantagem para ^ desta abordagem é que ele pode ser cérebro-fryingly difícil se você tiver muitos IFs', ou se você tem muitos que são aninhados
Don Cheadle
6

Use uma instrução CASE em vez de IF.

Joel Coehoorn
fonte
4

Você deseja a instrução CASE

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

Eu acho que onde ... como / = ... caso ... então ... pode trabalhar com booleanos. Estou usando o T-SQL.

Cenário: Digamos que você deseja obter os hobbies da Pessoa-30 se bool for falso e os hobbies da Pessoa-42 se bool for verdadeiro. (De acordo com alguns, as pesquisas por hobby compreendem mais de 90% dos ciclos de computação dos negócios, portanto, preste muita atenção.)

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
fonte
2
WHERE (IsNumeric (@OrderNumber) <> 1 OU OrderNumber = @OrderNumber) 
             AND (IsNumber (@OrderNumber) = 1 OU OrderNumber LIKE '%' 
                                              + @OrderNumber + '%')
WhoIsNinja
fonte
Regra de reescrita da Forma Normal Conjuntiva:IF P THEN Q ELSE R <=> ( ( NOT P ) OR Q ) AND ( P OR R )
onedaywhen
1

Instrução CASE é a melhor opção do que SE 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 
Majedur Rahaman
fonte
1
    WHERE OrderNumber LIKE CASE WHEN IsNumeric(@OrderNumber) = 1 THEN @OrderNumber ELSE  '%' + @OrderNumber END

No caso de linha, a condição funcionará corretamente.

Jubayer Hossain
fonte
0

O exemplo a seguir executa uma consulta como parte da expressão booleana e, em seguida, executa blocos de instrução ligeiramente diferentes com base no resultado da expressão booleana. Cada bloco de instrução começa com BEGIN e termina com 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

Usando instruções IF ... ELSE aninhadas O exemplo a seguir mostra como uma instrução IF… ELSE pode ser aninhada dentro de outra. Defina a variável @Number como 5, 50 e 500 para testar cada instrução.

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
fonte
2
Isso não parece relevante. Ele não usa um IF (ou qualquer código condicional) em uma cláusula WHERE.
precisa saber é o seguinte
0

No servidor sql, eu tinha o mesmo problema. Eu queria usar uma instrução e apenas se o parâmetro for falso e, em verdadeiro, eu tinha que mostrar os valores verdadeiro e falso, então usei dessa maneira

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

Veja se isso ajuda.

user2164001
fonte
-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
fonte
Isso não parece relevante. Ele não usa um IF (ou qualquer código condicional) em uma cláusula WHERE.
precisa saber é o seguinte