155 votes

Switch/Case SQL dans la clause 'where' (où)

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

J'essaie de faire ça 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 n'arrive pas à obtenir une syntaxe correcte, même de loin. SQL continue à se plaindre de mon '=' sur la première ligne WHEN...

Comment puis-je le faire ?

201voto

Bob Probst Points 4502
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

1 votes

Comme TomH l'a noté dans le commentaire de votre réponse ci-dessous, vous avez formé le SQL de manière incorrecte. J'ai testé le mien dans SQLServer 2005 et il a bien fonctionné.

0 votes

Pourquoi les @ sont-ils nécessaires ? Que font-ils ?

3 votes

@ indique des variables en t-sql, sans le @ , @locationID serait interprété comme un nom de colonne.

76voto

Lukek Points 450

Sans une 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)

2 votes

Le résultat sera légèrement différent, car l'instruction Case se termine une fois la condition remplie, mais la syntaxe OR évaluera toutes les possibilités.

1 votes

@tember Même si SQL était un langage procédural, si le premier OU est vrai, le reste de l'expression n'est pas évalué. Puisque SQL est un langage déclaratif, comment savez-vous que la DBM évaluera tous les ORs ? Question sincère, je ne comprends pas.

1 votes

En SQL, le reste de l'expression est évalué dans la syntaxe OR. Essayez ceci (je n'ai pas pu 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 égal à 0, mais elle continue à vérifier s'il est numérique, ce qui est le cas, et l'instruction renvoie donc une erreur.

39voto

Pittsburgh DBA Points 2342

Voilà.

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

5 votes

J'aurais écrit la formule suivante : 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)

2 votes

C'est une grande paix d'exemple, que diriez-vous du plan d'exécution de la requête avec la meilleure réponse ( personnellement je préfère cette méthode le code est clair et propre )

1 votes

Vraiment génial si vous voulez utiliser différentes clauses where avec différents types comme int sur la première clause et nvarchar sur la seconde. avec la solution de Bob Probst, cela ne fonctionne pas. merci.

6voto

Mark S. Rasmussen Points 13313

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 des tables différentes, ce qui vous permettrait de faire des requêtes beaucoup plus riches et d'éviter d'avoir des colonnes superflues.

Si vous n'êtes pas en mesure de modifier la structure, quelque chose comme ce qui suit pourrait 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 modifier la structure de la requête à la volée, mais nous pouvons la contourner en rendant les prédicats égaux entre eux.

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

0 votes

Je ne pense pas qu'il s'agisse d'une structure de table défectueuse. La table a été configurée de cette façon afin qu'elle soit auto-référencée pour avoir une quantité infinie de relations parent/enfant. Croyez-moi, c'était voulu. Je ne pense pas que je veuille changer la structure de ma table pour n'utiliser qu'une instruction de commutation, ce n'est pas si important.

6voto

Dillie-O Points 16780

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

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

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

donc dans votre cas particulier, vous allez devoir mettre la requête dans une procédure stockée ou créer trois requêtes séparées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X