SQL Server CASE WHEN and IN construction
Having a problem with a WHERE search statement would like to use a construction like..
WHERE f.foo IN
CASE @bar
WHEN 'BAR' THEN
('FOO','BAR',BAZ')
WHEN 'BAZ' THEN
('FOOBAR'开发者_Python百科,'FOOBAZ')
END
or
WHERE CASE @bar
WHEN 'BAR' THEN
f.foo IN ('FOO','BAR',BAZ')
WHEN 'BAZ' THEN
f.foo IN ('FOOBAR','FOOBAZ')
END
where @bar is a well defined temp variable of the correct type and all that f is defined nicely..
I get an error about "Error at ','
WHERE (@bar = 'BAR' and f.foo IN ('FOO', 'BAR', 'BAZ')) OR
(@bar = 'BAZ' and f.foo IN ('FOOBAR', 'FOOBAZ'))
SELECT *
FROM …
WHERE @bar = 'BAR'
AND foo IN ('FOO', 'BAR', 'BAZ')
UNION ALL
SELECT *
FROM …
WHERE @bar = 'BAZ'
AND foo IN ('FOOBAR', 'FOOBAZ')
This will be most index efficient.
SQL Server
will just optimize out one of the queries, depending on the value of @bar
, and will use the index on foo
to execute the remaining query.
Update:
Table master
has 20,000,000
records with 2,000,000
records having name = 't'
.
This query:
DECLARE @s INT
SET @s = 2
SELECT *
FROM master
WHERE (@s = 1 AND name IN ('t')) OR
(@s = 2 AND name IN ('zz'))
uses an INDEX SCAN
and returns nothing in 4
seconds:
|--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'))
This query:
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')
uses CONCATENATION
of two separate queries (one of them being optimized out), and returns instantly:
|--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)
You might drop the case part of the query. For Example:
WHERE ((@bar = 'BAR') AND (f.foo IN ('FOO','BAR','BAZ')))
OR ((@bar = 'BAZ') AND (f.foo in ('FOOBAR', 'FOOBAZ')))
As a wild guess, could it be that you're missing a ' in this line:
f.foo IN ('FOO','BAR',BAZ')
it should be
f.foo IN ('FOO','BAR','BAZ')
Case is an expression and not a statement.
WHERE CASE @bar WHEN 'BAR' THEN f.foo IN ('FOO','BAR','BAZ') WHEN 'BAZ' THEN f.foo IN ('FOOBAR','FOOBAZ') END
You missed a ' before BAZ
I don't believe that you can do such a construction, so you are stuck with something like:
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'))
or:
where @bar + '_' + f.foo in
('BAR_FOO', 'BAR_BAR', 'BAR_BAZ', 'BAZ_FOOBAR', 'BAZ_FOOBAZ')
CASE statement only allows for scalar output. You might want to handle it this way
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
You can do something on this line:
WHERE (@bar='BAR' AND f.foo IN ('FOO','BAR','BAZ'))
OR (@bar='BAZ' AND f.foo IN ('FOOBAR','FOOBAZ'))
It's also important that you understand why your snippets do not work (apart from mismatched quotes and other syntaxs errors). The CASE statement is not a control flow structure. It doesn't choose a branch of code and inserts it into your SQL. On the contrary, it evaluates its contents and returns a expression, just like a function call.
精彩评论