如何在sql中获取每个句点的开始日期和结束日期?

时间:2017-07-13 03:24:50

标签: sql sql-server

我有这样的表格行。

status     start      end
32         1/1/2017   1/2/2017
32         1/2/2017   4/2/2017
1          4/2/2017   6/3/2017
1          6/3/2017   9/5/2017
32         9/5/2017   19/5/2017
32         19/5/2017  22/6/2017


我想把行分组到

status     start      end
32         1/1/2017   4/2/2017
1          4/2/2017   9/5/2017
32         9/5/2017   22/6/2017


我该如何使用SQL?
谢谢你的帮助。

3 个答案:

答案 0 :(得分:1)

我认为你不能一步一步地做到这一点。也许如果你采用一些丑陋的递归CTE或一长串CTE或嵌套子查询。基本上,您需要重新配置数据集,以便了解期间的开始和结束。

假设:

  • 任何差距都意味着一段时间结束,一段新时期开始
  • 没有重叠的时期。 (即1(1/7 - 1/12),1(1/10 - 1/20))

我将使用SQL-Server语法,因为它是我最熟悉的,但这些操作应该是你可以在大多数sql环境中做一些修改的事情。 (我使用临时表和CTE,但您可以使用子查询)

create table dbo.[Status] (
    [status] int,
    [start] date,
    [end] date
)

insert into dbo.[Status] ([status], [start], [end])
values
    (32,    '20170101', '20170201'),
    (32,    '20170201', '20170204'),
    (1,     '20170204', '20170306'),
    (1,     '20170306', '20170509'),
    (32,    '20170509', '20170519'),
    (32,    '20170519', '20170622')


create table dbo.Result (
    PeriodID int identity, -- to make grouping and self-referential joins easier
    Status int,
    start date,
    next_start date null,
    [end] date null
)

select * from dbo.[Status]
-- This will get you all the periods and their start dates
;with cteExcludeTheseRows(status, start) as (
    -- These are the records where the Start date matches an end date for the same status group. As a result these aren't real periods, just extensions.
    select S.status, S.start
        from [Status] S
        join [Status] Prior on S.Status = Prior.status and S.start = Prior.[end]
)
insert into dbo.Result (status, start)
    select
        S.status,
        S.start
    from [Status] S
        left join cteExcludetheserows Exclude on S.status = Exclude.status and S.start = Exclude.start
    where Exclude.status is null

-- Reference the temp table to get the next start period for your status groups, that way you know that the end date for that period has to be less then that date
;with cteNextStart (PeriodID, next_start) as (
    select
        R.PeriodID, 
        next_start = min(next.start)
        from dbo.Result R
        join dbo.Result next on R.status = next.status and r.start < next.start
        group by R.PeriodID
)
update R
    set R.next_start = next.next_start
    from dbo.Result R
    join cteNextStart next on R.PeriodID = next.PeriodID

-- Now get the end date for each period by looking back at your main status table
;with cteEnd (PeriodID, [end]) as (
    select 
        R.PeriodID,
        [end] = max(s.[end])
        from dbo.Result R
        join [Status] S on R.status = s.status and S.[end] between R.start and isnull(R.next_start, '99991231')
        group by R.PeriodID
)
update R
    set R.[end] = [end].[end]
    from dbo.Result R
    join cteEnd [end] on R.PeriodID = [end].PeriodID


-- And finally, here you have your result set
select
    status,
    start,
    [end]
    from dbo.Result 
    order by start, status

drop table dbo.[Status]
drop table dbo.Result

答案 1 :(得分:1)

另见 demo

SELECT  * FROM aaa a
where a.sstatus != (select top 1 b.sstatus from aaa b
                                where b.start_date < a.start_date
                                order by b.start_date desc);

答案 2 :(得分:0)

Object.getPrototypeOf(d)