将日期范围细分为每个日历年的月份数

时间:2019-03-12 13:47:37

标签: sql sql-server tsql sql-server-2016

我有大量的日期范围,如下所示,例如。我需要计算每个实际日历年中有多少个月。因此,它会分解为:


合同: 123

开始日期: 2016年1月11日

结束日期:2018年1月6日

  • 2016年11月1日> 2016年12月31日-2016年为2个月
  • 2017年1月1日> 2017年12月31日-2017年为12个月
  • 01/01/2018> 01/06/2018-2018年的6个月

合同: 456

开始日期: 2017年5月31日

结束日期:2019年1月6日

  • 2017年5月31日> 2017年12月31日-2017年为6个月
  • 2018年1月1日> 2018年12月31日-2018年12个月
  • 2019年1月1日> 2019年1月6日-2019年6个月

有人知道解决此问题的方法吗?每个合同都有一行,都在同一表中,并且开始日期和结束日期在同一行中。

我原本是沿着CTE路线走的,但这令我震惊。

预期结果:

contract_id    year    number of months
123            2016    2
123            2017    12
123            2018    6
456            2017    6
456            2018    12
456            2019    6

或类似,我很乐意修改我的原始查询,以纳入实现此目标的最佳结果/方法。


表定义:

  • contract_id:整数
  • 开始日期:日期时间
  • 结束日期:datetime

    contract_id开始日期结束日期 123 2016-01-11 00:00:00.000 2018-06-01 00:00:00.000 456 2017-05-31 00:00:00.000 2019-06-01 00:00:00.000

2 个答案:

答案 0 :(得分:1)

我会为此做一个提示。我将一个放在我的系统上,以使其视线很快。这是视图。

create View [dbo].[cteTally] as

WITH
    E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
    E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
    E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
    cteTally(N) AS 
    (
        SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
    )
select N from cteTally
GO

然后,我们需要一些样本数据。像这样的东西。

declare @Something table
(
    Contract char(1)
    , StartDate date
    , EndDate date
)

insert @Something values
('A', '20161101', '20180601')
, ('B', '20170531', '20190601')

现在,我们可以查询样本并利用理货表格简化此工作。

select s.Contract
    , ContractYear = datepart(year, DATEADD(month, t.N - 1, s.StartDate))
    , NumMonths = count(*)
from @Something s
join cteTally t on t.N <= datediff(month, s.StartDate, s.EndDate) + 1
group by s.Contract
    , datepart(year, DATEADD(month, t.N - 1, s.StartDate))
order by s.Contract
    , datepart(year, DATEADD(month, t.N - 1, s.StartDate))

答案 1 :(得分:0)

有一种可能性:

select t.contract_id,n.id as year,q2.[#months]
from yourtable t
cross apply
(
    select year([Start Date]) as first_year,
    select year([End Date]) as last_year
)q
inner join numbers_table n on n.id between q.first_year and q.last_year
cross apply
(
    select case
        when n.id=first_year then 12-month([Start Date])
        when n.id=last_year then month([End Date])
        else 12
        end as [#months]
)q2

如果没有数字表,请将其放在查询之前:

;WITH numbers_table(id) AS
(
  SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1
  FROM sys.all_columns AS s1
  CROSS JOIN sys.all_columns AS s2
)

我会去测试Sean Lange的变体