SQL Switch / Case en la cláusula 'where'

146

Traté de buscar, pero no pude encontrar nada que me ayudara.

Estoy tratando de hacer esto en 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

Sé que no debería tener que poner '= @locationID' al final de cada uno, pero no puedo obtener la sintaxis ni siquiera cerca de ser correcta. SQL sigue quejándose de mi '=' en la primera línea CUANDO ...

¿Cómo puedo hacer esto?

Millas
fuente

Respuestas:

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
fuente
1
Como TomH señaló en el comentario a su respuesta a continuación, formó el SQL incorrectamente. Probé el mío en SQLServer 2005 y funcionó bien.
Bob Probst
¿Por qué se necesitan los @? ¿Qué hacen?
Tadej
2
@ indica variables en t-sql, sin @, @locationID se interpretaría como un nombre de columna.
Christian Sloper
70

sin una declaración 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
fuente
2
Esta es dará un resultado ligeramente diferente que se cumplan las salidas declaración del caso después de una condición - pero la sintaxis O evaluará todas las posibilidades
bre de
1
@tember Incluso si SQL fuera un lenguaje de procedimiento, si el primer OR es verdadero, entonces el resto de la expresión no se evalúa. Dado que SQL es un lenguaje declarativo, ¿cómo sabe que DBM evaluará todos los OR? Pregunta sincera, no entiendo.
ArturoTena
En SQL, el resto de la expresión se evalúa en la sintaxis OR. Intente esto (no me dejaría incluir el símbolo @; tendrá que corregirlo si desea probarlo): declare var varchar (5) set var = '0' select 2 / var donde var <> 0 o ISNUMERIC (var) = 1. Quiero que la condición salga porque var ES igual a 0, pero sigue adelante para verificar si es numérico, cuál es, y por lo tanto, la declaración devuelve un error.
tember
este me ayudó en mi caso donde tenía un operador de comparación diferente para cada valor del tipo.
Alex
mejor aún 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
35

Aqui tienes.

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
fuente
55
Bueno, lo habría escrito como SELECT column1, column2 FROM viewWhatever WHERE (@locationType = 'location' AND account_location = @locationID) OR (@locationType = 'area' AND xxx_location_area = @locationID) OR (@locationType = 'division' Y xxx_location_division = @locationID)
Jan de Vos
2
este es un gran ejemplo de paz, ¿qué tal el plan de ejecución de consultas con la mejor respuesta (personalmente prefiero que este código de método sea claro y limpio)
PEO
1
realmente genial si quieres usar una cláusula where diferente con un tipo diferente como int en la primera cláusula y nvarchar en la segunda. con la solución Bob Probst no funciona. gracias
Julian50
6

Yo diría que este es un indicador de una estructura de tabla defectuosa. Quizás los diferentes tipos de ubicación deberían estar separados en diferentes tablas, lo que le permite realizar consultas mucho más completas y también evitar tener columnas superfluas.

Si no puede cambiar la estructura, algo como lo siguiente podría 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
    )

Y así sucesivamente ... No podemos cambiar la estructura de la consulta sobre la marcha, pero podemos anularla haciendo que los predicados se igualen.

EDITAR: Las sugerencias anteriores son, por supuesto, mucho mejores, solo ignora las mías.

Mark S. Rasmussen
fuente
No creo que esta sea una estructura de tabla defectuosa. La tabla se configuró de esta manera para que fuera auto-referencia tener una cantidad infinita de relaciones padre / hijo. Créeme, fue a propósito. No creo que quiera cambiar la estructura de mi tabla para usar solo una declaración de cambio. no es tan importante
Miles
5

El problema con esto es que cuando el motor SQL va a evaluar la expresión, verifica la parte DE para extraer las tablas adecuadas, y luego la parte de DÓNDE para proporcionar algunos criterios básicos, por lo que no puede evaluar adecuadamente una condición dinámica en qué columna comprobar en contra.

Puede usar una cláusula WHERE cuando verifica los criterios WHERE en el predicado, como

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

entonces, en su caso particular, necesitará poner la consulta en un procedimiento almacenado o crear tres consultas separadas.

Dillie-O
fuente
5

El operador O puede ser una alternativa de caso cuando está en condiciones

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
fuente
3

Por favor intente esta consulta. Respuesta a la publicación anterior:

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
fuente
2
Para quien lea esto en el futuro: es lo mismo que la respuesta aceptada de @ bob-prost arriba: stackoverflow.com/a/206500/264786
ArturoTena
2

Prueba esto:

WHERE (
    @smartLocationType IS NULL 
    OR account_location = (
         CASE
            WHEN @smartLocationType IS NOT NULL 
                 THEN @smartLocationType
            ELSE account_location 
         END
    )
)
shah134pk
fuente
1
Votado porque no entiendo cómo esto podría elegir entre las cadenas dadas (es decir, 'ubicación', 'área', 'división')
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
fuente
-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
fuente
44
Voto negativo porque este contestador no está relacionado con la pregunta. La pregunta era cómo usar CASE en la cláusula WHERE, no cómo usar CASE en una columna SELECTed.
ArturoTena
-2

Prueba esta consulta. Es muy 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
fuente
1
Para quien lea esta respuesta: es lo mismo que la respuesta aceptada de @ bob-prost arriba: stackoverflow.com/a/206500/264786 Votado por esta razón.
ArturoTena