我正在尝试在我正在处理的查询的where子句中编写一个case语句。我正在导入Crystal报表中的代码,我基本上试图说明变量'type'是否设置为'create'在where子句中为此日期范围运行,否则运行不同的日期范围。它一直给我一个错误。我似乎无法确定我的语法有什么问题。帮助任何人?
DECLARE @Date1 DATETIME
DECLARE @Date2 DATETIME
DECLARE @type VARCHAR(20)
SET @Date1 = '2010-1-1'
SET @Date2 = '2010-2-1'
SET @type = '{?DateType}'
select *
from filled
WHERE
(CASE WHEN @type = 'create' THEN
filled.CREATEDON >= @Date1
AND filled.CREATEDON < DATEADD(d, +1, @Date2)
WHEN @type <> 'create' THEN
filled.datefilled >= @Date1
AND filled.datefilled < DATEADD(d, +1, @Date2)
END)
答案 0 :(得分:13)
您不需要case
声明
WHERE ( (@type = 'create' and filled.CREATEDON >= @Date1 AND filled.CREATEDON < DATEADD(d, +1, @Date2) ) or
(@type <> 'create' and filled.datefilled >= @Date1 AND filled.datefilled < DATEADD(d, +1, @Date2) )
)
这留下了case
语句中的非语义逻辑。这两个条件对@type
具有相同的值。我认为这是一个错字。
答案 1 :(得分:8)
在where子句中使用CASE表达式是可能的,但通常可以避免,并使用AND/OR
重写,在您的情况下,它将是:
WHERE( @Type = 'create'
AND filled.CREATEDON >= @Date1
AND filled.CREATEDON < DATEADD(d, +1, @Date2)
)
OR ( @Type != 'create'
AND filled.datefilled >= @Date1
AND filled.datefilled < DATEADD(d, +1, @Date2)
)
HOWEVER 这样的查询通常会产生欠佳的计划。如果可能,您应该使用IF/ELSE
逻辑:
IF @Type = 'create'
BEGIN
SELECT *
FROM Filled
WHERE Filled.CreatedOn >= @Date1
AND Filled.CreatedOn < DATEADD(DAY, 1, @Date2)
END
ELSE
BEGIN
SELECT *
FROM Filled
WHERE Filled.DateFilled >= @Date1
AND Filled.DateFilled < DATEADD(DAY, 1, @Date2)
END
原因是@type的值在编译时是未知的,因此优化器不知道它是否需要搜索DateFilled
或CreatedOn
,因此无法使用列中的索引(如果存在),因此无论可用索引如何,都将执行表扫描。然而,如果将逻辑与IF/ELSE
分开并且@type的值是什么并不重要,则为IF
的每个分支创建一个计划,并且在每个分支中,优化器知道哪个列将是搜索,并可以计划使用适当的索引。
您还可以使用UNION ALL
:
SELECT *
FROM Filled
WHERE Filled.CreatedOn >= @Date1
AND Filled.CreatedOn < DATEADD(DAY, 1, @Date2)
AND @Type = 'create'
UNION ALL
SELECT *
FROM Filled
WHERE Filled.DateFilled >= @Date1
AND Filled.DateFilled < DATEADD(DAY, 1, @Date2)
AND @Type <> 'create';
同样,如果DateFilled
或CreatedOn
上存在索引,则生成使用它们的计划的可能性要大于使用OR
的计划。