我有一张包含以下数据的表格:
如果某个类型的日期重叠,我想为重叠期间返回一个单独的行,最后会得到以下结果:
答案 0 :(得分:6)
我现在忽略PKey
,因为我不确定它是否与问题实际相关。
这解决了这个问题:
declare @t table (PKey int,Start date,[End] date,Type char(1))
insert into @t(PKey,Start,[End],Type) values
(1,'20100101','20100114','S'),
(2,'20100110','20100131','S'),
(3,'20100105','20100130','A'),
(4,'20100124','20100206','A'),
(5,'20100120','20100127','T'),
(6,'20100128','20100130','T')
;With EndDates as (
select [End],Type from @t
union all
select DATEADD(day,-1,Start),Type from @t
), Periods as (
select Type,MIN(Start) as Start,
(select MIN([End]) from EndDates e
where e.Type = t.Type and
e.[End] >= MIN(Start)) as [End]
from
@t t
group by Type
union all
select p.Type,DATEADD(day,1,p.[End]),e.[End]
from
Periods p
inner join
EndDates e
on
p.Type = e.Type and
p.[End] < e.[End]
where
not exists (select * from EndDates e2 where
e2.Type = p.Type and
e2.[End] > p.[End] and
e2.[End] < e.[End])
)
select * from Periods
order by Type,Start
首先,我们创建一个名为EndDates
的CTE,其中包含可能是一段时间结束的所有日期 - 这些日期是我们数据中已有的结束日期,或者它们是当天的结束日期在我们的数据中的一个开始日期之前。
然后我们建立期间 - 首先我们找到任何特定类型的第一个期间 - 我们采用最早的开始日期,以及开始日期之后的最早可能的结束日期。
然后,递归地,我们通过在现有结束后的第二天开始新的期间来建立额外的期间,并找到该日期之后的最早结束日期。
然后,基本上,我们已经完成了。结果:
Type Start End
---- ---------- ----------
A 2010-01-05 2010-01-23
A 2010-01-24 2010-01-30
A 2010-01-31 2010-02-06
S 2010-01-01 2010-01-09
S 2010-01-10 2010-01-14
S 2010-01-15 2010-01-31
T 2010-01-20 2010-01-27
T 2010-01-28 2010-01-30
哪个与您问题中的内容完全匹配,但我认为2月30日结束的A
行是拼写错误。
(我建议您重命名End
列,因为对列名使用保留字会变成真正的痛苦)
答案 1 :(得分:0)
考虑将范围视为几何线,然后使用SQL Server的几何工具。
首先,我需要将您的数据放入一个可以使用的表中:
declare @spans table (
type char(1),
start datetime,
stop datetime
);
insert @spans values
('S', '2010-01-01', '2010-01-14'),
('S', '2010-01-10', '2010-01-31'),
('A', '2010-01-05', '2010-01-30'),
('A', '2010-01-24', '2010-02-06'),
('T', '2010-01-20', '2010-01-27'),
('T', '2010-01-28', '2010-01-30');
不幸的是,SQL Server的空间工具在一方面受到限制。当您需要查询“几何”值中的单个形状或点时,必须传递索引号才能获取它。在Microsoft推出输出所有形状的TVF之前,我们一直使用数字表来完成工作。
用您喜欢的方法将其交换出来以创建数字表:
declare @numbers table (i int);
insert @numbers
select i = row_number() over (order by (select null))
from @spans a, @spans b;
现在您已准备好进行主查询。
在“ geoAggregates”中:
在外部查询中:
这里是代码。它会按预期输出,除了在“ A”的第二行中键入o之外。
with
geoAggregates as (
select type,
lines = geometry::UnionAggregate(line),
splitters = geometry::UnionAggregate(splitters)
from @spans
cross apply (select
startF = convert(float, start),
stopF = convert(float, stop) + 1
) prepare
cross apply (select
startP = geometry::Point(startF, 0, 0),
stopP = geometry::Point(stopF, 0, 0)
) pointify
cross apply (select
line = startP.STUnion(stopP).STEnvelope(),
splitters = startP.STUnion(stopP)
) lineify
group by type
)
select type,
start,
stop
from geoAggregates ga
cross apply (select
splitted = ga.lines.STDifference(splitters.STBuffer(0.001))
) sp
join @numbers n on n.i between 1 and sp.splitted.STNumGeometries()
cross apply (select
line = sp.splitted.STGeometryN(i).STEnvelope()
) l
cross apply (select
start = convert(datetime, round(l.line.STPointN(1).STX,0)),
stop = convert(datetime, round(l.line.STPointN(3).STX - 1,0))
) dateify
order by type, start;