我有大量的日期范围,如下所示,例如。我需要计算每个实际日历年中有多少个月。因此,它会分解为:
合同: 123
开始日期: 2016年1月11日
结束日期:2018年1月6日
合同: 456
开始日期: 2017年5月31日
结束日期:2019年1月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
或类似,我很乐意修改我的原始查询,以纳入实现此目标的最佳结果/方法。
表定义:
结束日期: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
答案 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的变体