【问题标题】:How do I return 60 sets of month and year data in a month and year column?如何在月和年列中返回60组月和年数据?
【发布时间】:2018-04-04 19:38:55
【问题描述】:

我提供了两个整数类型的变量,@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 循环——但它们似乎真的效率低下。我认为这不是最难的,但我想为它写“好”的代码。一段时间以来,我一直在思考“最好”的方式。

不幸的是,我对数据如何到达我没有任何发言权,这是我需要给出的回报,否则我会使用日期数据类型和日期函数。

【问题讨论】:

  • 最好总是主观的。我相信有人会发布一个很棒的 CTE 或维度表,使其成为一个简单的选择,带有联合或简单的交叉连接。是什么让您认为循环的性能会成为问题?我敢打赌,它们在运行之间基本上是相同且不一致的。
  • 我认为这只是被告知要完全避免光标或循环的问题。
  • 对于大型内存操作,这是真的,但这似乎很小。给你做个交易。你向我展示你的循环解决方案,我将提供一个非循环解决方案。 :)
  • 我绝对会用循环解决这个问题。与尝试提出“更好”解决方案所花费的时间相比,任何性能损失都将是最小的。 “完全避免循环”是在初学者准备好理解为什么应该避免循环以及何时不需要避免循环之前告诉他们的话。
  • 这是一个很好的观点,不过这很有趣,在我办公室的编码标准中,有一行指的是光标,简单地说是“CURSORS - 应该避免”,仅此而已。我希望对你们有点幽默。

标签: sql-server sql-server-2017


【解决方案1】:

我的偏好是将其保存在表格中。基本上,根据需要创建一个表,然后让它进入大约 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

这样做的额外好处是,由于它是持久的,如果您需要保存下游生成的任何数据,您可以在此处引用主键。也更容易理解发生了什么。

【讨论】:

  • 您的查询不会产生预期的结果:“...and year ends to 2005”。但如果业务允许,我确实同意持久化表的策略。
  • 你提到表的持久化很有趣,因为我花了太多时间试图提出一个最佳解决方案,我发现我们做了一个月/年的持久表。这是一个很好的解决方案,因为我不知道 datefromparts 并且在我的搜索中没有出现。
【解决方案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

【讨论】:

  • 再次阅读 OP 的第二段。他可能应该在“months”之后加一个逗号。
  • 感谢您的宝贵时间。有趣的是,我在深处偶然发现了一张持久化的桌子。我没有听说过维度表这个词,但这看起来就像我们正在处理的那样。我真的很感谢你的时间和精力,我学到了一些新东西。
  • 不用担心@Soulfire,希望你们能击败维拉诺瓦:(
  • 一样!但是运行仍然很神奇:)。谢谢你的一切!
猜你喜欢
  • 1970-01-01
  • 2020-12-21
  • 2019-05-22
  • 2013-02-12
  • 1970-01-01
  • 2021-03-10
  • 1970-01-01
  • 2020-06-08
  • 1970-01-01
相关资源
最近更新 更多