Commutateur / cas SQL dans la clause 'where'

146

J'ai essayé de chercher, mais je n'ai rien trouvé qui puisse m'aider.

J'essaye de faire ceci 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

Je sais que je ne devrais pas avoir à mettre '= @locationID' à la fin de chacun d'eux, mais je ne peux pas obtenir la syntaxe même près d'être correcte. SQL continue de se plaindre de mon '=' sur la première ligne WHEN ...

Comment puis-je faire ceci?

Miles
la source

Réponses:

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
la source
1
Comme TomH l'a noté dans le commentaire de votre réponse ci-dessous, vous avez mal formé le SQL. J'ai testé le mien dans SQLServer 2005 et cela a bien fonctionné.
Bob Probst
Pourquoi les @ sont-ils nécessaires? Que font-ils?
Tadej
2
@ indique des variables dans t-sql, sans @, @locationID serait interprété comme un nom de colonne.
Christian Sloper
70

sans déclaration de cas ...

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
la source
2
Ceci est donnera un résultat légèrement différent que les sorties de déclaration de cas après une condition est remplie - mais la syntaxe OU évaluera toutes les possibilités
tembre
1
@tember Même si SQL était un langage procédural, si le premier OR est vrai, le reste de l'expression n'est pas évalué. Puisque SQL est un langage déclaratif, comment savez-vous que le DBM évaluera tous les OR? Question sincère, je ne comprends pas.
ArturoTena
En SQL, le reste de l'expression est évalué dans la syntaxe OR. Essayez ceci (cela ne me laisserait pas inclure le symbole @ - vous devrez le corriger si vous voulez le tester): declare var varchar (5) set var = '0' select 2 / var where var <> 0 or ISNUMERIC (var) = 1. Je veux que la condition se termine parce que var EST égale à 0, mais elle vérifie si elle est numérique, ce qui est le cas, et par conséquent, l'instruction renvoie une erreur.
tember
celui-ci a aidé dans mon cas où j'avais un opérateur de comparaison différent pour chaque valeur du type.
Alex
mieux encore 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

Voici.

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
DBA de Pittsburgh
la source
5
Eh bien, j'aurais écrit cela comme SELECT column1, column2 FROM viewWhatever WHERE (@locationType = 'location' AND account_location = @locationID) OU (@locationType = 'area' AND xxx_location_area = @locationID) OU (@locationType = 'division' AND xxx_location_division = @locationID)
Jan de Vos
2
c'est une grande paix d'exemple, que diriez-vous du plan d'exécution de requête avec la meilleure réponse (personnellement, je préfère que ce code de méthode soit clair et propre)
PEO
1
vraiment génial si vous voulez utiliser une clause where différente avec un type différent comme int sur la 1ère clause et nvarchar sur la 2ème. avec la solution Bob Probst ne fonctionne pas. merci
Julian50
6

Je dirais que c'est un indicateur d'une structure de table défectueuse. Peut-être que les différents types d'emplacement devraient être séparés dans différentes tables, ce qui vous permet d'effectuer des requêtes beaucoup plus riches et d'éviter également d'avoir des colonnes superflues.

Si vous ne parvenez pas à modifier la structure, quelque chose comme ci-dessous peut fonctionner:

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
    )

Et ainsi de suite ... Nous ne pouvons pas changer la structure de la requête à la volée, mais nous pouvons la remplacer en égalisant les prédicats.

EDIT: Les suggestions ci-dessus sont bien sûr bien meilleures, ignorez les miennes.

Mark S. Rasmussen
la source
Je ne pense pas que ce soit une structure de table défectueuse. La table a été mise en place de cette façon afin que ce soit des auto-références pour avoir une quantité infinie de relations parent / enfant. Croyez-moi, c'était exprès. Je ne pense pas que je veuille changer la structure de ma table pour simplement utiliser une instruction switch. son nto that important
Miles
5

Le problème avec ceci est que lorsque le moteur SQL va évaluer l'expression, il vérifie la partie FROM pour extraire les tables appropriées, puis la partie WHERE pour fournir des critères de base, de sorte qu'il ne peut pas correctement évaluer une condition dynamique sur quelle colonne vérifier contre.

Vous pouvez utiliser une clause WHERE lorsque vous vérifiez les critères WHERE dans le prédicat, tels que

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

Ainsi, dans votre cas particulier, vous devrez placer la requête dans une procédure stockée ou créer trois requêtes distinctes.

Dillie-O
la source
5

L'opérateur OR peut être une alternative au cas où la condition

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
la source
3

Veuillez essayer cette requête. Réponse au message ci-dessus:

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
la source
2
À quiconque lit ceci à l'avenir: c'est la même chose que la réponse acceptée de @ bob-prost ci-dessus: stackoverflow.com/a/206500/264786
ArturoTena
2

Essaye ça:

WHERE (
    @smartLocationType IS NULL 
    OR account_location = (
         CASE
            WHEN @smartLocationType IS NOT NULL 
                 THEN @smartLocationType
            ELSE account_location 
         END
    )
)
shah134pk
la source
1
Voté contre parce que je ne comprends pas comment cela pourrait choisir entre les chaînes données (c'est-à-dire 'location', 'area', 'division')
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
la source
-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
la source
4
Évalué car cette réponse n'est pas liée à la question. La question était de savoir comment utiliser CASE sur la clause WHERE, et non comment utiliser CASE sur une colonne SELECTed.
ArturoTena
-2

Essayez cette requête. C'est très facile à comprendre:

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
la source
1
À quiconque lit cette réponse: c'est la même chose que la réponse acceptée de @ bob-prost ci-dessus: stackoverflow.com/a/206500/264786 Vote contre le vote pour cette raison.
ArturoTena