Switch / Case SQL na cláusula 'where'

146

Tentei procurar ao redor, mas não consegui encontrar nada que me ajudasse.

Estou tentando fazer isso no SQL:

declare @locationType varchar(50);
declare @locationID int;

SELECT column1, column2
FROM viewWhatever
WHERE
CASE @locationType
    WHEN 'location' THEN account_location = @locationID
    WHEN 'area' THEN xxx_location_area = @locationID
    WHEN 'division' THEN xxx_location_division = @locationID

Eu sei que não deveria ter que colocar '= @locationID' no final de cada um, mas não consigo obter a sintaxe nem perto de estar correta. O SQL continua reclamando do meu '=' na primeira linha WHEN ...

Como posso fazer isso?

Milhas
fonte

Respostas:

191
declare @locationType varchar(50);
declare @locationID int;

SELECT column1, column2
FROM viewWhatever
WHERE
@locationID = 
  CASE @locationType
      WHEN 'location' THEN account_location
      WHEN 'area' THEN xxx_location_area 
      WHEN 'division' THEN xxx_location_division 
  END
Bob Probst
fonte
1
Como TomH observou no comentário à sua resposta abaixo, você formou o SQL incorretamente. Testei o meu no SQLServer 2005 e funcionou bem.
Bob Probst
Por que o @ é necessário? O que eles fazem?
Tadej
2
@ indica variáveis ​​no t-sql, sem o @, @locationID seria interpretado como um nome de coluna.
Christian Sloper
70

sem uma declaração de caso ...

SELECT column1, column2
FROM viewWhatever
WHERE
    (@locationType = 'location' AND account_location = @locationID)
    OR
    (@locationType = 'area' AND xxx_location_area = @locationID)
    OR
    (@locationType = 'division' AND xxx_location_division = @locationID)
Lukek
fonte
2
Esta é a vontade de dar um resultado ligeiramente diferente como as saídas caso declaração após uma condição for atendida - mas o OR sintaxe irá avaliar todas as possibilidades
bro de
1
Mesmo que o SQL fosse uma linguagem procedural, se o primeiro OR for verdadeiro, o restante da expressão não será avaliada. Como SQL é uma linguagem declarativa, como você sabe que o DBM avaliará todos os ORs? Pergunta sincera, eu não entendo.
ArturoTena
No SQL, o restante da expressão é avaliado na sintaxe OR. Tente isso (ele não me permitirá incluir o símbolo @ - você precisará corrigi-lo se quiser testá-lo): declare var varchar (5) set var = '0' selecione 2 / var onde var <> 0 ou ISNUMERIC (var) = 1. Eu quero que a condição saia porque var IS é igual a 0, mas ele avança para verificar se é numérico, qual é e, portanto, a instrução retorna um erro.
bro de
este ajudou no meu caso, onde eu tinha um operador de comparação diferente para cada valor do tipo.
21416 Alex
melhor ainda DECLARE @locationType NVARCHAR(50) = 'youchoose' IF @locationType = 'location' BEGIN SELECT column1, column2 FROM viewWhatever WHERE (account_location = @locationID) END IF @locationType = 'area' BEGIN SELECT column1, column2 FROM viewWhatever WHERE (xxx_location_area = @locationID) END IF @locationType = 'division' BEGIN SELECT column1, column2 FROM viewWhatever WHERE (xxx_location_division = @locationID) END
Lukek 27/01
35

Aqui está.

SELECT
   column1, 
   column2
FROM
   viewWhatever
WHERE
CASE 
    WHEN @locationType = 'location' AND account_location = @locationID THEN 1
    WHEN @locationType = 'area' AND xxx_location_area = @locationID THEN 1
    WHEN @locationType = 'division' AND xxx_location_division = @locationID THEN 1
    ELSE 0
END = 1
Pittsburgh DBA
fonte
5
Bem, eu teria escrito isso como SELECT column1, column2 FROM viewWhatever WHERE (@locationType = 'location' AND account_location = @locationID) OR (@locationType = 'area' AND xxx_location_area = @locationID) OR (@locationType = 'division' E xxx_location_division = @locationID)
Jan de Vos
2
esta é uma grande paz de exemplo, como sobre o plano de execução de consulta com o melhor respondida (pessoalmente, prefiro este código método é clara e limpa)
PEO
1
realmente ótimo se você quiser usar uma cláusula where diferente com um tipo diferente, como int na 1ª cláusula e nvarchar na 2ª. com solução Bob Probst é não funciona. graças
Julian50
6

Eu diria que este é um indicador de uma estrutura defeituosa da tabela. Talvez os diferentes tipos de local devam ser separados em tabelas diferentes, permitindo que você faça consultas muito mais avançadas e também evite ter colunas supérfluas.

Se você não conseguir alterar a estrutura, algo como o abaixo pode funcionar:

SELECT
    *
FROM
    Test
WHERE
    Account_Location = (
        CASE LocationType
          WHEN 'location' THEN @locationID
          ELSE Account_Location
        END
    )
    AND
    Account_Location_Area = (
        CASE LocationType
          WHEN 'area' THEN @locationID
          ELSE Account_Location_Area
        END
    )

E assim por diante ... Não podemos alterar a estrutura da consulta em tempo real, mas podemos substituí-la fazendo com que os predicados se igualem.

EDIT: As sugestões acima são obviamente muito melhores, apenas ignore as minhas.

Mark S. Rasmussen
fonte
Eu não acho que essa seja uma estrutura de tabela defeituosa. A tabela foi configurada dessa maneira, de forma que fosse auto-referência para ter uma quantidade infinita de relações pai / filho. Acredite, foi de propósito. Acho que não quero alterar minha estrutura de tabela para usar apenas uma instrução switch. não é tão importante
Miles
5

O problema disso é que, quando o mecanismo SQL avalia a expressão, ele verifica a parte FROM para obter as tabelas apropriadas e, em seguida, a parte WHERE para fornecer alguns critérios básicos, para que não possa avaliar adequadamente uma condição dinâmica na qual coluna verifique contra.

Você pode usar uma cláusula WHERE ao verificar os critérios WHERE no predicado, como

WHERE account_location = CASE @locationType
                              WHEN 'business' THEN 45
                              WHEN 'area' THEN 52
                         END

portanto, no seu caso particular, você precisará colocar a consulta em um procedimento armazenado ou criar três consultas separadas.

Dillie-O
fonte
5

O operador OR pode ser uma alternativa de caso quando em que condição

ALTER PROCEDURE [dbo].[RPT_340bClinicDrugInventorySummary]
    -- Add the parameters for the stored procedure here
     @ClinicId BIGINT = 0,
     @selecttype int,
     @selectedValue varchar (50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT
    drugstock_drugname.n_cur_bal,drugname.cdrugname,clinic.cclinicname

FROM drugstock_drugname
INNER JOIN drugname ON drugstock_drugname.drugnameid_FK = drugname.drugnameid_PK
INNER JOIN drugstock_drugndc ON drugname.drugnameid_PK = drugstock_drugndc.drugnameid_FK
INNER JOIN drugndc ON drugstock_drugndc.drugndcid_FK = drugndc.drugid_PK
LEFT JOIN clinic ON drugstock_drugname.clinicid_FK = clinic.clinicid_PK

WHERE   (@ClinicId = 0 AND 1 = 1)
    OR  (@ClinicId != 0 AND drugstock_drugname.clinicid_FK = @ClinicId)

    -- Alternative Case When You can use OR
    AND ((@selecttype = 1 AND 1 = 1)
    OR  (@selecttype = 2 AND drugname.drugnameid_PK = @selectedValue)
    OR  (@selecttype = 3 AND drugndc.drugid_PK = @selectedValue)
    OR  (@selecttype = 4 AND drugname.cdrugclass = 'C2')
    OR  (@selecttype = 5 AND LEFT(drugname.cdrugclass, 1) = 'C'))

ORDER BY clinic.cclinicname, drugname.cdrugname
END
atik sarker
fonte
3

Por favor, tente esta consulta. Resposta para post acima:

select @msgID, account_id
    from viewMailAccountsHeirachy
    where 
    CASE @smartLocationType
        WHEN 'store' THEN account_location
        WHEN 'area' THEN xxx_location_area 
        WHEN 'division' THEN xxx_location_division 
        WHEN 'company' THEN xxx_location_company 
    END  = @smartLocation
Durre Najaf
fonte
2
Para quem lê este no futuro: é o mesmo que a resposta aceita de @ bob-prost acima: stackoverflow.com/a/206500/264786
ArturoTena
2

Tente o seguinte:

WHERE (
    @smartLocationType IS NULL 
    OR account_location = (
         CASE
            WHEN @smartLocationType IS NOT NULL 
                 THEN @smartLocationType
            ELSE account_location 
         END
    )
)
shah134pk
fonte
1
Downvoted porque eu não entendo como isso pode escolher entre as cordas dadas (ie 'localização', 'área', 'divisão')
ArturoTena
0
CREATE PROCEDURE [dbo].[Temp_Proc_Select_City]
    @StateId INT
AS  
        BEGIN       
            SELECT * FROM tbl_City 
                WHERE 
                @StateID = CASE WHEN ISNULL(@StateId,0) = 0 THEN 0 ELSE StateId END ORDER BY CityName
        END
Darshan Balar
fonte
-1
Case Statement in SQL Server Example

Syntax

CASE [ expression ]

   WHEN condition_1 THEN result_1
   WHEN condition_2 THEN result_2
   ...
   WHEN condition_n THEN result_n

   ELSE result

END

Example

SELECT contact_id,
CASE website_id
  WHEN 1 THEN 'TechOnTheNet.com'
  WHEN 2 THEN 'CheckYourMath.com'
  ELSE 'BigActivities.com'
END
FROM contacts;

OR

SELECT contact_id,
CASE
  WHEN website_id = 1 THEN 'TechOnTheNet.com'
  WHEN website_id = 2 THEN 'CheckYourMath.com'
  ELSE 'BigActivities.com'
END
FROM contacts;
kavitha Reddy
fonte
4
Voto negativo porque este respondedor não está relacionado à pergunta. A questão era como usar CASE na cláusula WHERE, não como usar CASE em uma coluna SELECTed.
ArturoTena
-2

Tente esta consulta. É muito fácil de entender:

CREATE TABLE PersonsDetail(FirstName nvarchar(20), LastName nvarchar(20), GenderID int);
GO

INSERT INTO PersonsDetail VALUES(N'Gourav', N'Bhatia', 2),
              (N'Ramesh', N'Kumar', 1),
              (N'Ram', N'Lal', 2),
              (N'Sunil', N'Kumar', 3),
              (N'Sunny', N'Sehgal', 1),
              (N'Malkeet', N'Shaoul', 3),
              (N'Jassy', N'Sohal', 2);
GO

SELECT FirstName, LastName, Gender =
    CASE GenderID
    WHEN 1 THEN 'Male'
    WHEN 2 THEN 'Female'
    ELSE 'Unknown'
    END
FROM PersonsDetail
Mike Clark
fonte
1
Para quem lê esta resposta: é a mesma que a resposta aceita de @ bob-prost acima: stackoverflow.com/a/206500/264786 A votação foi reduzida por esse motivo.
ArturoTena