TSQL按顺序循环几个月

时间:2015-05-12 15:59:19

标签: sql-server tsql

我有一个问题,我感觉不到我的深度。

我需要在两个日期之间循环几个月,并返回每个月的数据子集,并且空白行数月,没有数据。

例如:

TransactionID    |    Date          |    Value
1                |    01/01/2015    |    £10
2                |    16/01/2015    |    £15
3                |    21/01/2015    |    £5
4                |    15/03/2015    |    £20
5                |    12/03/2015    |    £15
6                |    23/04/2015    |    £10

需要返回:

Month            |    Amount
January          |    £30
February         |    £0
March            |    £35
April            |    £10

我的查询将依赖于指定日期范围,以便我可以设置查询的第一个和最后一个日期。

我觉得我可能过度思考这个问题,但已经到了那个阶段,你开始觉得自己陷入了困境。

4 个答案:

答案 0 :(得分:3)

关键是可以访问整数列表来表示范围内的月份。如果你没有Numbers Table,那么spt_values就会紧张。

SqlFiddle Demo

SELECT
  [Year]   = YEAR(DATEADD(month,[i],@range_start))
 ,[Month]  = DATENAME(month,DATEADD(month,[i],@range_start))
 ,[Amount] = ISNULL(SUM([Value]),0)
FROM (
  SELECT TOP (DATEDIFF(month,@range_start,@range_end)+1)
    ROW_NUMBER() OVER(ORDER BY (SELECT 1))-1 [i]
  FROM master.dbo.spt_values
) t1
LEFT JOIN #MyTable t2
  ON (t1.[i] = DATEDIFF(month,@range_start,t2.[Date]) )
GROUP BY [i]
ORDER BY [i]

答案 1 :(得分:2)

SQL起初是一种棘手的语言。你实际上不想要一个循环。实际上,除了极少数情况外,你几乎不想在SQL中循环。试试这个:

DECLARE @StartDate  DATE,
        @EndDate    DATE;

SET @StartDate  = '01 January 2015';
SET @EndDate    = '30 April 2015';

WITH CTE_Months
AS
(
    SELECT @StartDate dates
    UNION ALL
    SELECT DATEADD(MONTH,1,dates)
    FROM CTE_Months
    WHERE DATEADD(MONTH,1,dates) < @EndDate
)

SELECT  YEAR(B.[date]) AS yr,
        DATENAME(MONTH,B.[Date]) AS month_name,
        SUM(ISNULL(B.Value,0)) AS Amount
FROM CTE_Months A
LEFT JOIN yourTable B
ON  YEAR(A.[date]) = YEAR(B.[date])
    AND MONTH(A.[date]) = MONTH(B.[date])
GROUP BY YEAR(B.[date]),DATENAME(MONTH,B.[Date])

答案 2 :(得分:0)

一种方法:创建一个名为months的表格,其中包含monthnum int字段和12行[1..12]

declare @start date = '01 jan 2015',
        @end   date = '30 apr 2015'

select 
    datename(month, dateadd(month, monthnum, 0) - 1),
    isnull(Amount, 0)
from months
left join (
    select
        month(date) Month,
        sum(Value) Amount
    from tbl 
    where date between @start and @end
    group by month(date)
) T on (T.Month = months.monthnum)
where months.monthnum between month(@start) and month(@end)
order by monthnum

答案 3 :(得分:0)

The following code will generate one output row for each month between the first and last transaction dates. Spanning a year boundary, or multiple years, is handled correctly.

[link]