如何在月份和年份列中返回60组月份和年份数据?

时间:2018-04-04 19:38:56

标签: sql-server sql-server-2017

我提供了两个变量,@month@year整数类型(因此月份是1,2,3,... 9,10,11,12和年份是四位数年)。

我要做的是创建一个表值函数,它将接收这两个参数并返回过去60个月,然后年终结束到2005年。

因此,例如,对于@month = 1@year = 2018,我希望返回此表:

+----+------+
| 1  | 2018 |
+----+------+
| 12 | 2017 |
+----+------+
| 11 | 2017 |
+----+------+
| 10 | 2017 |
+----+------+
| 9  | 2017 |
+----+------+
| 8  | 2017 |
+----+------+
| 7  | 2017 |
+----+------+
| 6  | 2017 |
+----+------+
| 5  | 2017 |
+----+------+
| 4  | 2017 |
+----+------+
| 3  | 2017 |
+----+------+
| 2  | 2017 |
+----+------+
| 1  | 2017 |
+----+------+
| 12 | 2016 |
+----+------+
| 11 | 2016 |
+----+------+
| 10 | 2016 |
+----+------+
| 9  | 2016 |
+----+------+
| 8  | 2016 |
+----+------+
| 7  | 2016 |
+----+------+
| 6  | 2016 |
+----+------+
| 5  | 2016 |
+----+------+
| 4  | 2016 |
+----+------+
| 3  | 2016 |
+----+------+
| 2  | 2016 |
+----+------+
| 1  | 2016 |
+----+------+
| 12 | 2015 |
+----+------+
| 11 | 2015 |
+----+------+
| 10 | 2015 |
+----+------+
| 9  | 2015 |
+----+------+
| 8  | 2015 |
+----+------+
| 7  | 2015 |
+----+------+
| 6  | 2015 |
+----+------+
| 5  | 2015 |
+----+------+
| 4  | 2015 |
+----+------+
| 3  | 2015 |
+----+------+
| 2  | 2015 |
+----+------+
| 1  | 2015 |
+----+------+
| 12 | 2014 |
+----+------+
| 11 | 2014 |
+----+------+
| 10 | 2014 |
+----+------+
| 9  | 2014 |
+----+------+
| 8  | 2014 |
+----+------+
| 7  | 2014 |
+----+------+
| 6  | 2014 |
+----+------+
| 5  | 2014 |
+----+------+
| 4  | 2014 |
+----+------+
| 3  | 2014 |
+----+------+
| 2  | 2014 |
+----+------+
| 1  | 2014 |
+----+------+
| 12 | 2013 |
+----+------+
| 11 | 2013 |
+----+------+
| 10 | 2013 |
+----+------+
| 9  | 2013 |
+----+------+
| 8  | 2013 |
+----+------+
| 7  | 2013 |
+----+------+
| 6  | 2013 |
+----+------+
| 5  | 2013 |
+----+------+
| 4  | 2013 |
+----+------+
| 3  | 2013 |
+----+------+
| 2  | 2013 |
+----+------+
| 12 | 2012 |
+----+------+
| 12 | 2011 |
+----+------+
| 12 | 2010 |
+----+------+
| 12 | 2009 |
+----+------+
| 12 | 2008 |
+----+------+
| 12 | 2007 |
+----+------+
| 12 | 2006 |
+----+------+
| 12 | 2005 |
+----+------+

我正在考虑采用这种方式,或许使用交叉连接,或者使用一些while循环 - 但它们似乎效率很低。我不认为这是最难做到的,但我想为它编写“好”的代码。很长一段时间以来,我一直在思考“最好”的方式。

不幸的是,我对数据的来源没有任何发言权,这是我需要提供的回报,否则我会使用日期数据类型和日期函数。

2 个答案:

答案 0 :(得分:2)

这使用递归CTE。当然,如果您的环境允许,我只使用dimension table。这是一种方式,但最佳方式当然是有争议的。

declare @year int = 2018
declare @month int = 1



;with cte as(
    select [Month], [Year] = 2005
    from  (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) as X([Month]) 
    union all
    select [Month], [Year] + 1
    from cte
    where [Year] < @year
),

someYears as(
select [Years] = 2005
union all
select [Years] + 1
from someYears
where [Years] < @year),

t as(
select  *, RN = row_number() over (order by [Year] desc, [Month] desc) 
from cte
where [Month] <= case when [Year] = @year then @month else 13 end)


select [Month], [Year]
from t
where RN <= 60
union 
select 12, [Years]
from (select [Years] from someYears) x
where x.Years < @year
order by [Year] desc, [Month] desc

答案 1 :(得分:2)

我倾向于将其保留在表格中。基本上,创建一个尽可能远的桌子,然后让它在未来50年左右。然后,您的函数可以从该表中选择。

表:

create table MonthDim
(
PK_ID int identity(1, 1) primary key,
Month int,
Year int,
Dt date --set this to the first of each month
)

查询:

select *
from MonthDim
where dt >= dateadd(month, -60, datefromparts(@year, @month, 1))
    and dt <= datefromparts(@year, @month, 1)

union all

select *
from MonthDim
where dt < dateadd(month, -60, datefromparts(@year, @month, 1))
    and Month = 12

这样做的另一个好处是,由于它是持久的,如果你需要保存下游生成的任何数据,你可以在这里引用主键。它也更容易理解正在发生的事情。