0

Please look the following query:

SELECT ID, START, END FROM TABLEA

the result is:

ID       START                      END
1        2012-06-07 19:32:00        2012-06-08 06:00:00
2        2012-06-08 06:00:00        NULL

But I want really a result like this:

START                      END
2012-06-07 19:32:00        2012-06-07 23:59:59
2012-06-08 00:00:00        NULL

The id no matters.

And look at this example:

 ID       START                      END
    1        2012-06-06 19:32:00        2012-06-08 06:00:00
    2        2012-06-08 06:00:00        NULL

It Must be:

        START                      END
        2012-06-06 19:32:00        2012-06-06 23:59:59
        2012-06-07 00:00:00        2012-06-07 23:59:59
        2012-06-08 00:00:00        NULL

The idea is to generate rows with the start and end values within the same day.

Thanks in advance.

4

2 回答 2

1

The following solution does everything you want, all in SQL. It merges overlapping and contiguous timespans (including wholly-overlapping timespans) and also breaks out multi-day timespans by date using a calendar table.

declare @TimeSheetEntries table 
    ( 
    ID int identity not null primary key, 
    [Start] datetime not null, 
    [End] datetime null 
    ); 

declare @CalendarTable table
(
    CalDate datetime
)

DECLARE @CurDate datetime = '2000-01-01 00:00:00';

WHILE @CurDate <= '2020-12-31'
BEGIN
    INSERT INTO @CalendarTable
    SELECT @CurDate;

    SELECT @CurDate = DATEADD(day,1,@CurDate);
END

insert into @TimeSheetEntries 
    ( 
    [Start], 
    [End] 
    ) 
select 
    '2012-06-06 19:32:00', 
    '2012-06-08 06:00:00' 
union all select 
    '2012-06-08 06:00:00', 
    NULL
union all select 
    '2012-06-08 02:00:00', 
    '2012-06-08 03:00:00' 
union all select 
    '2012-01-01 14:09:00', 
    '2012-01-01 17:30:00'
union all select
    '2012-01-01 18:30:00', 
    '2012-01-01 19:30:00';

WITH ClockData AS
(
    SELECT ID, [Start], [End] AS EffectiveEnd, [Start] AS LastStart, 1 AS NumTimespans
    FROM @TimeSheetEntries ts
    WHERE NOT EXISTS 
    (
        SELECT tsWhere.ID FROM @TimeSheetEntries tsWhere 
        WHERE
            tsWhere.ID <> ts.ID -- don't match yourself!
            AND tsWhere.[Start] < ts.[Start]
            AND
            (
                -- Three types of things we don't want our outer SELECT to return
                -- 1) Completed timespans with another appointment's end date sitting inside;
                -- 2) Incomplete timespans (NULL ClockedOut) with another appointment's end date after ClockedIn
                -- 3) Any timespan wholly inside another one
                (ts.[End] IS NULL AND tsWhere.[End] >= ts.[Start]) OR
                (ts.[End] IS NOT NULL AND tsWhere.[End] BETWEEN ts.[Start] AND ts.[End]) OR
                (tswhere.[End] > ts.[End])
            )
    )

    UNION ALL

    SELECT cd.ID, cd.[Start], ts.[End] AS EffectiveClockout, ts.Start AS LastStart, cd.NumTimespans + 1 AS NumTimespans
    FROM @TimeSheetEntries ts
    INNER JOIN ClockData cd
            ON ts.[Start] BETWEEN cd.[Start] AND cd.EffectiveEnd AND ts.ID <> cd.ID 
                AND (ts.[End] IS NULL OR ts.[End] > cd.EffectiveEnd)
),
MergedTimespans AS
(
    SELECT [ID], [Start], [EffectiveEnd], [LastStart]
    FROM ClockData cd
    WHERE NumTimespans = (SELECT MAX(NumTimespans) FROM ClockData WHERE ID = cd.ID)
)
SELECT 
    CASE 
        WHEN mt.Start > cal.CalDate 
            THEN mt.Start 
        ELSE cal.CalDate 
    END AS [Start],
    CASE 
        WHEN (mt.EffectiveEnd IS NOT NULL AND mt.EffectiveEnd >= DATEADD(day,1,cal.CalDate)) OR (mt.EffectiveEnd IS NULL AND cal.CalDate < CAST(mt.LastStart AS date)) THEN DATEADD(second, -1,DATEADD(day,1,cal.CalDate))
        ELSE mt.EffectiveEnd
    END AS [End]
FROM MergedTimespans mt
    INNER JOIN @CalendarTable cal
        ON (mt.EffectiveEnd IS NULL AND cal.CalDate BETWEEN CAST(mt.Start AS date) AND mt.LastStart) -- Handle NULL end dates
         OR (mt.EffectiveEnd IS NOT NULL AND cal.CalDate BETWEEN CAST(mt.Start AS date) AND mt.EffectiveEnd)
ORDER BY Start
于 2012-06-08T16:39:22.880 回答
0
 with       cte
      AS ( SELECT   MIN(START) AS MinStart ,
                    MAX(START) AS MaxStart
           FROM     TableA
           UNION ALL
           SELECT   DATEADD(DAY, 1, MinStart) ,
                    MaxStart
           FROM     cte
           WHERE    DATEADD(DAY, 1, MinStart) BETWEEN MinStart AND MaxStart
         )
SELECT  MinStart AS START ,
        DATEADD(millisecond, -3, FLOOR(CAST(MinStart AS FLOAT)) + 1) AS [End]
FROM    cte
于 2012-06-08T15:36:57.990 回答