我正在尝试构建一个查询,其中我需要应用2个不同的where子句,具体取决于Current Month的值。在这种情况下,我需要显示过去2年的数据,仅显示当月之前的数月:
当前日期为:01-01-2017
需要显示以下数据:
01/2015; 02/2015; 03/2015; 04/2015; 05/2015; 06/2015;
07/2015; 08/2015; 09/2015; 10/2015; 11/2015; 12/2015;
01/2016; 02/2016; 03/2016; 04/2016; 05/2016; 06/2016;
07/2016; 08/2016; 09/2016; 10/2016; 11/2016; 12/2016.
当前日期为:01-03-2017
需要显示来自:01/2016; 02/2016; 01/2017; 02/2017.
所以我构建了以下查询:
SELECT *
FROM TABLE1
WHERE
CASE MONTH(GETDATE())
WHEN 1
THEN YEAR(Data)>=YEAR(GETDATE())-2 and YEAR(data)<YEAR(GETDATE())
ELSE YEAR(Data)>=YEAR(GETDATE())-1 and YEAR(data)<=YEAR(data) and MONTH(data)<MONTH(GETDATE())
END
我收到了一个错误。 你能帮我么? 谢谢。
答案 0 :(得分:1)
您的语法肯定不正确。 THEN
不是逻辑表达式 - 它应该返回value
。因此,您无法在THEN
/ ELSE
块中编写逻辑表达式。相反,你可以尝试类似的东西:
WHERE
@date >= CASE WHEN a=b THEN '20150101' ELSE '20160202' END
另一件事是:谓词中的转换和函数对性能非常不利。处理日期时,您可能希望在查询之前尽可能准备过滤谓词,例如:
declare
@date_begin date,
@date_end date
set @date_end = DATEADD(..., @arg_date)
set @date_begin = DATEADD(YEAR, -2, @date_end)
select ...
where date between @date_begin and @date_end
在你的情况下它可能是这样的:
declare
@arg_date DATE = GETDATE(),
@date_begin DATE,
@date_end DATE,
@max_month INT
set @max_month = MONTH(@date)
if @max_month = 1
begin
set @date_end = DATEADD(dd, 1-DATEPART(dy, @arg_date), @arg_date) /* first day of year */
set @date_begin = dateadd(YY, -2, @date_end)
end
else
begin
set @date_end = @arg_date
set @date_begin = dateadd(YY, -1, DATEADD(dd, 1-DATEPART(dy, @date_end), @date_end)) /* first day of year_begin */
end
SELECT *
FROM TABLE1 t
WHERE t.date >= @date_begin and t.date < @date_end
AND (@max_month = 1 OR MONTH(t.date) < @max_month)
另一种(更好的)方法是准备@periods
表变量,将所需的每个(date_begin, date_end)
对放入其中并加入TABLE1
- 您将摆脱所有函数调用来自WHERE
子句。
您应该意识到:您确切地知道结果集中每年需要哪些时段。存储的TABLE1->date
列无需计算任何内容。只需使用预先计算的日期间隔进行过滤即可。不要转换或修改date
列 - 它已经可以使用了。仅适用适当的过滤器。 MONTH(date) <= 3
是date <= 20170331
。不要折磨左派 - 准备这些谓词中适当的正确部分。
答案 1 :(得分:0)
最简单的方法是:
SELECT *
FROM TABLE1
WHERE
(YEAR(Data)>=YEAR(GETDATE())-2 and YEAR(data)<YEAR(GETDATE()) AND MONTH(GETDATE()) = 1)
OR (YEAR(Data)>=YEAR(GETDATE())-1 and MONTH(data)<MONTH(GETDATE()) and MONTH(GETDATE()) <> 1)
(注意我删除了多余的and YEAR(data)<=YEAR(data)
。)。
我个人更喜欢(我认为通常建议)AND/OR
逻辑CASE
条款中的WHERE
。
CASE
语句的错误是由CASE
返回原子值的事实引起的。它在过程语言中的使用方式与if
不同。
答案 2 :(得分:0)
您无法使用where
语句将其他语句交换到case
子句。相反,您需要将case
解析为相等:
select *
from Table1
where case month(getdate()) -- You want to avoid using functions on fields in your WHERE claises, as this can reduce performance.
when 1 then case when Data >= dateadd(year,datediff(year,0,getdate())-2,0)
and Data < dateadd(year,datediff(year,0,getdate()),0)
then 1 -- Data rows the meet the criteria will return 1.
else 0 -- Data rows that do not will return 0.
end
else case when (Data >= dateadd(year,datediff(year,0,getdate())-1,0)
and Data < dateadd(m,datediff(m,0,getdate())-12,0)
)
or (Data >= dateadd(year,datediff(year,0,getdate()),0)
and Data < dateadd(m,datediff(m,0,getdate()),0)
)
then 1
else 0
end
end = 1 -- Then limit the results to only those rows that returned a 1.
但是,在您的特定实例中,可以将其简化为标准or
:
select *
from Table1
where (month(getdate()) = 1
and Data >= dateadd(year,datediff(year,0,getdate())-2,0)
and Data < dateadd(year,datediff(year,0,getdate()),0)
)
or (month(getdate()) <> 1
and (Data >= dateadd(year,datediff(year,0,getdate())-1,0)
and Data < dateadd(m,datediff(m,0,getdate())-12,0)
)
or (Data >= dateadd(year,datediff(year,0,getdate()),0)
and Data < dateadd(m,datediff(m,0,getdate()),0)
)
)
注意使用上面的括号来分隔出逻辑测试。如果数据行符合其中一个条件,则会在查询中返回。