SQL Server单独的重叠日期

问题描述 投票:0回答:2

我有一个包含以下数据的表:

<< img src =“ https://image.soinside.com/eyJ1cmwiOiAiaHR0cHM6Ly9pLnN0YWNrLmltZ3VyLmNvbS8wbUVSMi5wbmcifQ==” alt =“在此处输入图像描述”>

如果类型的日期重叠,那么我想在重叠期间返回单独的一行,因此最终得到以下结果:

“在此处输入图像描述”

sql sql-server database tsql
2个回答
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列,因为使用保留字作为列名可能会带来很大的麻烦)


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;
© www.soinside.com 2019 - 2024. All rights reserved.