根据一个字段的值应用不同的WHERE子句

时间:2017-01-11 11:40:52

标签: sql-server tsql case

我正在尝试构建一个查询,其中我需要应用2个不同的where子句,具体取决于Current Month的值。在这种情况下,我需要显示过去2年的数据,仅显示当月之前的数月:

示例1:

当前日期为: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.

示例2:

当前日期为: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

我收到了一个错误。 你能帮我么? 谢谢。

3 个答案:

答案 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) <= 3date <= 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)
             )
      )

注意使用上面的括号来分隔出逻辑测试。如果数据行符合其中一个条件,则会在查询中返回。