过滤日期记录

时间:2015-04-13 02:09:41

标签: sql sql-server date between

在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语句,它从任意月份开始返回x个月的数据。例如滚动3个月,滚动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. 这不起作用并抛出似乎是指BETWEEN条款的Error line 1: Conversion failed when converting date and/or time from character string..
  2. 这似乎是一种非常尴尬的做法和浪费资源。编写此查询的有效方法是什么?

4 个答案:

答案 0 :(得分:3)

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

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

以下示例非常有效,尤其是如果您可以在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:如果你能够通过年度&amp;单独月份,你可以运行:

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

答案 1 :(得分:2)

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

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

但这不会将您的年月作为日期返回。这是一项要求吗?

答案 2 :(得分:0)

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)

答案 3 :(得分:0)

我现在无法在这台机器上查看我的语法,但我会尝试这样的事情。基本上,我把你的开始月份和年份作为日期,并以01作为日期,然后我使用它以相同的方式将每一行与铸造进行比较,使用this answer中的技术。

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)