过滤日期记录

filter date records

在 SQL Server 2008 上,我有一个具有以下架构的视图 revenue

+----------------------------+ 
| id | year | month | amount | 
+----------------------------+ 
|  1 | 2014 |    11 |    100 | 
|  2 | 2014 |    12 |   3500 | 
|  3 | 2014 |    12 |     90 | 
|  4 | 2015 |     1 |   1000 | 
|  5 | 2015 |     2 |   6000 | 
|  6 | 2015 |     2 |    600 | 
|  7 | 2015 |     3 |     70 | 
|  8 | 2015 |     3 |    340 | 
+----------------------------+ 

上面的模式和数据已经过简化,视图非常大,有数百万行。我无法控制架构,因此无法将字段更改为 DATE 或类似字段。年月字段都是INT。

我正在寻找 SELECT 声明 returns 我从任意月份开始的 x 个月的数据。比如rolling 3个月,rolling 5个月等等

我想到的是这个:

SELECT
rolling_date,
amount

FROM (SELECT CAST('01/' + RIGHT('00' + CONVERT(VARCHAR(2), month), 2) + '/' + CAST(year AS VARCHAR(4)) AS DATE) AS rolling_date,
         amount
    FROM [revenue]
) date_revenue

WHERE rolling_date BETWEEN CAST('01/12/2014' AS DATE) AND CAST('31/02/2015' AS DATE)

然而,...

  1. 这不起作用并抛出 Error line 1: Conversion failed when converting date and/or time from character string.. 似乎指的是 BETWEEN 子句
  2. 这似乎是一种非常笨拙的方式,而且是一种资源浪费。编写此查询的有效方法是什么?

您可以对年月进行整数比较:

SELECT
 id, yr, month, amount
FROM
  Magazines
WHERE yr*100 + month >= 201412 AND yr*100 + month <= 201503

但这不会 return 您的年月作为日期。这是要求吗?

如果SQL服务器版本为2012及以上,您可以使用DATEFROMPARTS方法从年份和月份构造日期。如果不是,请将您的查询修改为:

SELECT
rolling_date,
amount

FROM (SELECT CAST(
  CAST(year AS VARCHAR(4)) +
  RIGHT('0' + CAST(month AS VARCHAR(2)), 2) +
  RIGHT('0' + CAST('01' AS VARCHAR(2)), 2) 
 AS DATE) AS rolling_date,
     amount
FROM [revenue]
) date_revenue

WHERE rolling_date BETWEEN CAST('2014/02/01' AS DATE) AND CAST('2015/12/31' AS DATE)

首先,转换错误的发生是因为没有 2 月 31 日。我在下面的示例中将其更改为 2 月 28 日。

由于您的 table 包含数百万行,因此您最好避免对 table 中的数据进行任何转换或计算。相反,将输入转换为与您的 table 相匹配的格式。这样您就可以利用索引。

以下示例将非常有效,尤其是如果您可以在 Year, Month 上创建非聚集索引。

declare @start datetime = '2014-12-01'
declare @end datetime = '2015-02-28'

declare @startyear int = datepart(year, @start)
declare @startmonth int = datepart(month, @start)

declare @endyear int = datepart(year, @end)
declare @endmonth int = datepart(month, @end)

select * from revenue
where (Year > @startyear OR (Year = @startyear AND Month >= @startmonth))
AND (Year < @endyear OR (Year = @endyear AND Month <= @endmonth))

编辑:从处理的角度来看,以下示例是相同的,并且没有声明任何新变量:

select * from revenue
where (Year > datepart(year, @start)
  OR (Year = datepart(year, @start) AND Month >= datepart(month, @start)))
AND (Year < datepart(year, @end)
  OR (Year = datepart(year, @end) AND Month <= datepart(month, @end)))

编辑 2:如果您能够单独传递年份和月份,您可以运行这样:

select * from revenue r
where (Year > 2014 OR (Year = 2014 AND Month >= 12))
AND (Year < 2015 OR (Year = 2015 AND Month <= 2))

我现在不能在这台机器上检查我的语法,但我会尝试这样的事情。基本上,我把你的开始月份和年份设为日期时间,以 01 为日期,然后我使用它以相同的方式对每一行进行比较,使用 this answer.[= 中的技术。 12=]

declare @n integer
set @n = 3

declare @startmonth as integer
set @startmonth = 12

declare @startyear as integer
set @startyear = 2014

declare @startdatetime as datetime
set @startdatetime = 
      CAST(@startyear AS VARCHAR(4)) +
      RIGHT('0' + CAST(@startmonth AS VARCHAR(2)), 2) +
      '01'
   AS DATETIME)

select
*
from revenue
where
CAST(
      CAST(year AS VARCHAR(4)) +
      RIGHT('0' + CAST(month AS VARCHAR(2)), 2) +
      '01'
   AS DATETIME) >= @startdatetime
and
CAST(
      CAST(year AS VARCHAR(4)) +
      RIGHT('0' + CAST(month AS VARCHAR(2)), 2) +
      '01'
   AS DATETIME)
<
dateadd(month, @n, @startdatetime)