我需要一个在SQL Server中合并日期的解决方案。
以下设置工作示例。我有两个表,第一个持有特定产品的单个期间,第二个持有与第一个表相关的期间内的较小期间。我需要将两者合并,以便最终得到一个包含较小时期的原始时期所有连续时期的列表。我已经包含了所需结果的样本。
规则:
declare @x table (product varchar(10), fromdate date, todate date) -- forecast declare @y table (product varchar(10), fromdate date, todate date) -- purchases declare @z table (product varchar(10), fromdate date, todate date) -- result insert @x values ('lrecs', '20150101', '20161231') insert @x values ('srecs', '20150701', '20161231') insert @y values ('lrecs', '20150401', '20150630') insert @y values ('lrecs', '20160101', '20160630') insert @y values ('srecs', '20160101', '20161231') /* product fromdate todate ------------------------------ lrecs 2015-01-01 2015-03-31 lrecs 2015-04-01 2015-06-30 lrecs 2015-07-01 2015-12-31 lrecs 2016-01-01 2016-06-30 lrecs 2016-07-01 2016-12-31 srecs 2015-07-01 2015-12-31 srecs 2016-01-01 2016-12-31 */
答案 0 :(得分:0)
TrimmedRanges
cte只使用表@x来“修剪”表@y中的每个范围。然后CombinedRanges
cte查找相邻范围并创建覆盖它们的新范围。它以递归方式执行此操作。最后,MostComprehensiveRanges
cte仅拉出其他范围未包含的范围。
注意,根据您正在处理的数据量,您可能需要使用OPTION (MAXRECURSION ##)
查询提示。
https://msdn.microsoft.com/en-us/library/ms175972.aspx
declare @x table (product varchar(10), fromdate date, todate date) -- forecast
declare @y table (product varchar(10), fromdate date, todate date) -- purchases
declare @z table (product varchar(10), fromdate date, todate date) -- result
insert @x values ('lrecs', '20150101', '20161231')
insert @x values ('srecs', '20150701', '20161231')
insert @y values ('lrecs', '20141201', '20150331')
insert @y values ('lrecs', '20150401', '20150630')
insert @y values ('lrecs', '20150701', '20150731')
insert @y values ('lrecs', '20150801', '20150831')
insert @y values ('lrecs', '20160101', '20160630')
insert @y values ('srecs', '20160101', '20161231')
;with TrimmedRanges as (
select
a.product,
case when a.fromdate < b.fromdate then b.fromdate else a.fromdate end fromdate,
case when a.todate > b.todate then b.todate else a.todate end todate
from
@y a
join @x b on a.product = b.product
),
CombinedRanges as (
select product, fromdate, todate from TrimmedRanges
union all
select a.product, a.fromdate, b.todate
from
TrimmedRanges a
join CombinedRanges b
on a.product = b.product
and b.fromdate = dateadd(d, 1, a.todate)
),
MostComprehensiveRanges as (
select a.*
from CombinedRanges a
where
not exists (
select 1
from CombinedRanges b
where
a.product = b.product
and (
( a.fromdate > b.fromdate and a.fromdate < b.todate )
or ( a.todate > b.fromdate and a.todate < b.todate )
)
)
)
select * from MostComprehensiveRanges
order by product, fromdate
答案 1 :(得分:0)
我承认我没有对此进行过广泛的测试,以确保它适用于所有情况,但它适用于您的示例数据集。它是一个递归/循环/游标解决方案。检查一下,如果需要任何调整或者您有任何问题,请告诉我。
--Combines the tables
;WITH CTE_Union
AS
(
SELECT product,fromdate,todate
FROM @y
UNION ALL
SELECT product,fromdate,NULL
FROM @x
UNION ALL
SELECT product,NULL,todate
FROM @x
),
--forms most of the ranges
CTE_range
AS
(
SELECT *,
next_fromdate = LEAD(fromdate,1) OVER (PARTITION BY product ORDER BY fromdate)
FROM
(
SELECT product,
fromdate = COALESCE(fromdate,DATEADD(DAY,1,LAG(todate,1) OVER (PARTITION BY product ORDER BY todate))),
todate = COALESCE(todate,DATEADD(DAY,-1,LEAD(fromdate,1) OVER (PARTITION BY product ORDER BY fromdate)))
FROM CTE_Union
) A
WHERE fromdate < todate
)
SELECT product,
fromdate,
todate
FROM CTE_range
UNION ALL
--fills in the gaps
SELECT product,DATEADD(DAY,1,todate),DATEADD(DAY,-1,next_fromdate)
FROM CTE_range
WHERE DATEDIFF(DAY,todate,next_fromdate) != 1 --so where there is a gap
ORDER BY product,fromdate