用 SQL 中的日期范围填补空白

Filling gaps with date ranges in SQL

我有一个事件 table,它有开始日期和结束日期列(事件不重叠),示例数据

if object_id('tempdb..#SourceTable') is not null
begin
    drop table #SourceTable
end

create table #SourceTable
(
    Id int identity(1,1) not null,
    WindowRange varchar(15) not null,
    StartDatetime datetime null,
    EndDatetime datetime null
)


insert into #SourceTable
(
    WindowRange,
    StartDatetime,
    EndDatetime
)
values 
    ('04:20 - 05:36', '2015-08-31 04:20:01.890', '2015-08-31 05:36:14.290' ),
    ('00:20 - 01:24', '2015-08-31 00:20:01.487', '2015-08-31 01:24:52.983' ),
    ('20:20 - 21:27', '2015-08-30 20:20:01.177', '2015-08-30 21:27:53.317' ),
    ('16:20 - 17:28', '2015-08-30 16:20:01.133', '2015-08-30 17:28:24.173' ),
    ('12:20 - 13:30', '2015-08-30 12:20:01.273', '2015-08-30 13:30:38.370' )

示例输出

Id  WindowRange     StartDatetime           EndDatetime
1   04:20 - 05:36   2015-08-31 04:20:01.890 2015-08-31 05:36:14.290
2   00:20 - 01:24   2015-08-31 00:20:01.487 2015-08-31 01:24:52.983
3   20:20 - 21:27   2015-08-30 20:20:01.177 2015-08-30 21:27:53.317
4   16:20 - 17:28   2015-08-30 16:20:01.133 2015-08-30 17:28:24.173
5   12:20 - 13:30   2015-08-30 12:20:01.273 2015-08-30 13:30:38.370

对于上面的示例,我想要额外的行来填充范围中的空白

预期输出

Id  WindowRange     StartDatetime           EndDatetime
1   04:20 - 05:36   2015-08-31 04:20:01.890 2015-08-31 05:36:14.290
2   01:24 - 04:20   2015-08-31 01:24:52.983 2015-08-31 04:20:01.890
3   00:20 - 01:24   2015-08-31 00:20:01.487 2015-08-31 01:24:52.983
4   00:00 - 00:20   2015-08-31 00:00:00.000 2015-08-31 00:20:01.487
5   21:27 - 23:59   2015-08-30 21:27:53.317 2015-08-30 23:59:59.999
6   20:20 - 21:27   2015-08-30 20:20:01.177 2015-08-30 21:27:53.317
7   17:28 - 20:20   2015-08-30 17:28:24.173 2015-08-30 20:20:01.177
8   16:20 - 17:28   2015-08-30 16:20:01.133 2015-08-30 17:28:24.173
9   13:30 - 16:20   2015-08-30 13:30:38.370 2015-08-30 16:20:01.133
10  12:20 - 13:30   2015-08-30 12:20:01.273 2015-08-30 13:30:38.370

我试过使用带有 window 函数的通用 table 表达式,但似乎无法正确使用

;with myCTE as
(
    select 
        row_number() over (order by EndDatetime desc) as SeqNo, 
        StartDatetime,
        EndDatetime
    from #SourceTable
)


select 
    t1.SeqNo as [T1SeqNo],
    t2.SeqNo as [T2SeqNo],
    t1.StartDatetime as [T1Start],
    t1.EndDatetime as [T1End],
    t2.StartDatetime as [T2Start],
    t2.EndDatetime as [T2End]
from myCTE t1
left join myCTE t2
    on t1.SeqNo = t2.SeqNo - 1

任何 suggestion/help 将不胜感激。

    ;with myCTE as
(
    select 
        row_number() over (order by EndDatetime desc) as SeqNo, 
        StartDatetime,
        EndDatetime
    from #SourceTable
)

select ROW_NUMBER() over (order by T1Start DESC), *
from (
select 
    t1.StartDatetime as [T1Start],
    t1.EndDatetime as [T1End]
from  myCTE t1
UNION ALL
select 
    t1.EndDatetime as [T1Start],
    t2.StartDatetime as [T1SEnd]
from  myCTE t1
inner join myCTE t2
    on t1.SeqNo = t2.SeqNo + 1

) as t
order by T1Start DESC
DECLARE @WindowRange varchar(15), 
        @StartDatetime datetime, 
        @EndDatetime datetime, 
        @StartDate date, 
        @EndDate date, 
        @NextStartDatetime datetime, 
        @NextEndDatetime datetime

DECLARE yourCursor CURSOR  FORWARD_ONLY READ_ONLY FOR
    SELECT  StartDatetime, EndDatetime FROM #SourceTable order by StartDatetime
OPEN yourCursor

FETCH NEXT FROM yourCursor INTO @StartDatetime, @EndDatetime
IF @@FETCH_STATUS = 0 
    BEGIN
        FETCH NEXT FROM yourCursor INTO @NextStartDatetime, @NextEndDatetime
        WHILE @@FETCH_STATUS = 0
            BEGIN
                SET @StartDate = @StartDatetime
                SET @EndDate = @EndDatetime
                IF @EndDate > @StartDate
                    BEGIN
                        SET @WindowRange = LEFT(CONVERT(varchar, @StartDatetime, 108), 5)  + ' - ' + LEFT(CONVERT(varchar, @EndDate, 108), 5)
                        INSERT INTO #SourceTable (WindowRange, StartDatetime, EndDatetime) VALUES (@WindowRange, @StartDatetime, @EndDate)


                        SET @WindowRange = LEFT(CONVERT(varchar, @StartDatetime, 108), 5)  + ' - ' + LEFT(CONVERT(varchar, @EndDate, 108), 5)
                        INSERT INTO #SourceTable (WindowRange, StartDatetime, EndDatetime) VALUES (@WindowRange, @EndDate, @EndDatetime)
                    END

                    SET @StartDate = @EndDatetime
                    SET @EndDate = @NextStartDatetime

                    IF @EndDate > @StartDate
                    BEGIN
                        SET @WindowRange = LEFT(CONVERT(varchar, @EndDatetime, 108), 5)  + ' - ' + '00:00' --@StartDate
                        INSERT INTO #SourceTable (WindowRange, StartDatetime, EndDatetime) VALUES (@WindowRange, @EndDatetime, @StartDate)


                        SET @WindowRange = '00:00'  + ' - ' + LEFT(CONVERT(varchar, @NextStartDatetime, 108), 5) --@EndDate
                        INSERT INTO #SourceTable (WindowRange, StartDatetime, EndDatetime) VALUES (@WindowRange, @EndDate, @NextStartDatetime)
                    END



                SET @WindowRange = LEFT(CONVERT(varchar, @EndDatetime, 108), 5)  + ' - ' + LEFT(CONVERT(varchar, @NextStartDatetime, 108), 5)
                INSERT INTO #SourceTable (WindowRange, StartDatetime, EndDatetime) VALUES (@WindowRange, @EndDatetime, @NextStartDatetime)
                SET @StartDatetime = @NextStartDatetime
                SET @EndDatetime = @NextEndDatetime
                FETCH NEXT FROM yourCursor INTO @NextStartDatetime, @NextEndDatetime
            END
        END
CLOSE yourCursor;
DEALLOCATE yourCursor;

select * from  #SourceTable order by StartDatetime

我制作了一个 SQL 服务器游标代码,几乎接近您的预期结果。这里的不同之处在于您期望“2015-08-30 23:59:59.999”的第 5 条记录,但在 SQL 服务器中将读取“2015-08-30 23:59:59.999”作为“2015-09-01 00:00:00.000”。希望这有帮助

DECLARE @temp TABLE(
    Id int identity(1,1) not null,
    WindowRange varchar(15) not null,
    StartDatetime datetime null,
    EndDatetime datetime null
)

INSERT INTO @temp
SELECT TOP 1 WindowRange, StartDatetime, EndDatetime 
FROM #SourceTable ORDER BY StartDateTime

DECLARE @curStartDateTime DATETIME
DECLARE @curEndDateTime DATETIME

DECLARE @prevStartDateTime DATETIME
DECLARE @prevEndDateTime DATETIME

DECLARE @iteration INT
SET @iteration = 0

DECLARE @timeRange CURSOR

SET @timeRange = CURSOR FOR
SELECT StartDatetime, EndDatetime FROM #SourceTable ORDER BY StartDateTime

OPEN @timeRange

FETCH NEXT FROM @timeRange INTO @curStartDateTime, @curEndDateTime

WHILE @@FETCH_STATUS = 0
BEGIN
    IF @iteration <> 0
    BEGIN
        IF CONVERT(VARCHAR(8), @curStartDateTime, 112) = CONVERT(VARCHAR(8), @prevEndDateTime, 112)
        BEGIN
            IF CONVERT(VARCHAR(23), @curStartDateTime, 121) <> CONVERT(VARCHAR(23), @prevEndDateTime, 121)
            BEGIN
                INSERT INTO @temp
                SELECT 
                    CONVERT(VARCHAR(5), @prevEndDateTime, 108) + ' - ' + CONVERT(VARCHAR(5), @curStartDateTime, 108)
                    , @prevEndDateTime
                    , @curStartDateTime
            END
        END
        ELSE
        BEGIN
            INSERT INTO @temp
                SELECT 
                    CONVERT(VARCHAR(5), @prevEndDateTime, 108) + ' - 23:59'
                    , @prevEndDateTime
                    , CONVERT(VARCHAR(8), @curStartDateTime, 112) + ' 23:59:59.999'

            INSERT INTO @temp
                SELECT 
                    '00:00 - ' + CONVERT(VARCHAR(5), @curStartDateTime, 108)
                    , CONVERT(VARCHAR(8), DATEADD(day, 1, @prevEndDateTime), 112) + ' 00:00:00.000'
                    , @curStartDateTime
        END

        INSERT INTO @temp
        SELECT 
            CONVERT(VARCHAR(5), @curStartDateTime, 108) + ' - ' + CONVERT(VARCHAR(5), @curEndDateTime, 108)
            , @curStartDateTime
            , @curEndDateTime
    END

    SET @prevStartDateTime = @curStartDateTime
    SET @prevEndDateTime = @curEndDateTime

    SET @iteration = @iteration + 1
    FETCH NEXT FROM @timeRange INTO @curStartDateTime, @curEndDateTime
END

CLOSE @timeRange
DEALLOCATE @timeRange

SELECT * FROM @temp ORDER BY StartDatetime DESC