2009-12-29 4 views
4

Un problème avec une instruction WHERE de recherche souhaite utiliser une construction comme ..SQL Server CASE WHEN et dans la construction

WHERE f.foo IN 
    CASE @bar 
     WHEN 'BAR' THEN 
     ('FOO','BAR',BAZ') 
     WHEN 'BAZ' THEN 
     ('FOOBAR','FOOBAZ') 
    END 

ou

WHERE CASE @bar 
     WHEN 'BAR' THEN 
     f.foo IN ('FOO','BAR',BAZ') 
     WHEN 'BAZ' THEN 
     f.foo IN ('FOOBAR','FOOBAZ') 
    END 

où @bar est une température bien définie variable du type et de tout ce qui f est défini bien ..

je reçois une erreur de « erreur à « »

+0

Ce n'est pas la perte 'qui a été mon deuxième taux de frappe. –

Répondre

5
WHERE (@bar = 'BAR' and f.foo IN ('FOO', 'BAR', 'BAZ')) OR 
     (@bar = 'BAZ' and f.foo IN ('FOOBAR', 'FOOBAZ')) 
+0

Je préférerais ceci pour l'amour de maintenance. S'il y a une autre condition sur quand @bar = 'BAR' c'est beaucoup plus lisible que l'imbrication qui résulterait de la structure dans la question. –

+0

Je choisirais aussi ceci au cours de la prochaine réponse, je me penche vers la vitesse, mais réalise que je ne verrai plus ce code dans le cycle de maintenance. Merci à tous. –

2

En tant que conjecture sauvage, pourrait-il que vous manque un »dans cette ligne:

f.foo IN ('FOO','BAR',BAZ') 

il devrait être

f.foo IN ('FOO','BAR','BAZ') 
0
WHERE CASE @bar 
     WHEN 'BAR' THEN 
     f.foo IN ('FOO','BAR','BAZ') 
     WHEN 'BAZ' THEN 
     f.foo IN ('FOOBAR','FOOBAZ') 
    END

Vous avez manqué un avant BAZ

2

Vous pouvez supprimer la partie cas de la requête. Par exemple:

WHERE ((@bar = 'BAR') AND (f.foo IN ('FOO','BAR','BAZ'))) 
OR ((@bar = 'BAZ') AND (f.foo in ('FOOBAR', 'FOOBAZ'))) 
1

Case est une expression et non une instruction.

3
SELECT * 
FROM … 
WHERE @bar = 'BAR' 
     AND foo IN ('FOO', 'BAR', 'BAZ') 
UNION ALL   
SELECT * 
FROM … 
WHERE @bar = 'BAZ' 
     AND foo IN ('FOOBAR', 'FOOBAZ') 

Ce sera le plus efficace indice.

SQL Server va simplement optimiser l'une des requêtes, en fonction de la valeur de @bar, et utilisera l'index sur foo pour exécuter la requête restante.

Mise à jour:

Tableau master a 20,000,000 enregistrements avec 2,000,000 enregistrements ayant name = 't'.

Cette requête:

DECLARE @s INT 
SET @s = 2 
SELECT * 
FROM master 
WHERE (@s = 1 AND name IN ('t')) OR 
     (@s = 2 AND name IN ('zz')) 

utilise un et ne retourne rien en 4 secondes:

|--Parallelism(Gather Streams) 
     |--Index Scan(OBJECT:([test].[dbo].[master].[ix_name_desc]), WHERE:([@s]=(1) AND [test].[dbo].[master].[name]='t' OR [@s]=(2) AND [test].[dbo].[master].[name]='zz')) 

Cette requête:

DECLARE @s INT 
SET @s = 2 
SELECT * 
FROM master 
WHERE @s = 1 AND name IN ('t') 
UNION ALL 
SELECT * 
FROM master 
WHERE @s = 2 AND name IN ('zz') 

utilise CONCATENATION de deux requêtes distinctes (l'une des les étant en cours d'optimisation), et revient instantanément:

|--Concatenation 
     |--Parallelism(Gather Streams) 
     | |--Filter(WHERE:(STARTUP EXPR([@s]=(1)))) 
     |   |--Index Seek(OBJECT:([test].[dbo].[master].[ix_name_desc]), SEEK:([test].[dbo].[master].[name]='t') ORDERED FORWARD) 
     |--Filter(WHERE:(STARTUP EXPR([@s]=(2)))) 
      |--Index Seek(OBJECT:([test].[dbo].[master].[ix_name_desc]), SEEK:([test].[dbo].[master].[name]='zz') ORDERED FORWARD) 
+0

+1. Je n'avais pas envisagé d'utiliser UNION dans ce type de cas. Je vais devoir tester cela en utilisant une condition OU pour voir lequel est le plus performant. Merci. – NotMe

+0

Merci d'avoir réalisé ce test. Vous venez de me convaincre que j'ai fait des choses très très mauvaises. :) – NotMe

0

Je ne crois pas que vous pouvez faire une telle construction, de sorte que vous êtes coincé avec quelque chose comme:

where 
    (@bar = 'BAR' and (f.foo = 'FOO' or f.foo = 'BAR' or f.foo = 'BAZ')) or 
    (@bar = 'BAZ' and (f.foo = 'FOOBAR' or f.foo = 'FOOBAZ')) 

ou:

where @bar + '_' + f.foo in 
    ('BAR_FOO', 'BAR_BAR', 'BAR_BAZ', 'BAZ_FOOBAR', 'BAZ_FOOBAZ') 
0

instruction CASE permet uniquement scalaire sortie. Vous pouvez gérer cette façon

WHERE CASE 
      WHEN @bar = 'BAR' AND @foo = 'FOO' THEN 1 
      WHEN @bar = 'BAR' AND @foo = 'BAR' THEN 1 
      WHEN @bar = 'BAR' AND @foo = 'BAZ' THEN 1 
      WHEN @bar = 'BAZ' AND @foo = 'FOOBAR' THEN 1 
      WHEN @bar = 'BAZ' AND @foo = 'FOOBAZ' THEN 1 
      ELSE 0 
     END = 1 
0

Vous pouvez faire quelque chose sur cette ligne:

WHERE (@bar='BAR' AND f.foo IN ('FOO','BAR','BAZ')) 
    OR (@bar='BAZ' AND f.foo IN ('FOOBAR','FOOBAZ')) 

Il est également important que vous compreniez pourquoi vos extraits ne fonctionnent pas (en dehors des citations dépareillées et d'autres syntaxs les erreurs). L'instruction CASE n'est pas une structure de flux de contrôle. Il ne choisit pas une branche de code et l'insère dans votre SQL. Au contraire, il évalue son contenu et renvoie une expression, tout comme un appel de fonction.

Questions connexes