基于迭代集的方法:
-- Sample data.
declare @TimesheetEntries as Table ( Id Int Identity, StaffId Int, ClockIn DateTime, ClockOut DateTime )
insert into @TimesheetEntries ( StaffId, ClockIn, ClockOut ) values
( 4, '2012-05-03 09:00', '2012-05-03 12:00' ),
( 4, '2012-05-03 13:30', '2012-05-03 17:30' ), -- This falls within 2 hours of the next two rows.
( 4, '2012-05-03 17:35', '2012-05-03 18:00' ),
( 4, '2012-05-03 19:00', '2012-05-03 19:30' ),
( 4, '2012-05-03 19:45', '2012-05-03 20:00' ),
( 5, '2012-05-03 09:00', '2012-05-03 12:00' ),
( 5, '2012-05-03 14:09', '2012-05-03 17:30' ),
( 6, '2012-05-03 09:00', '2012-05-03 12:00' ),
( 6, '2012-05-03 13:00', '2012-05-03 17:00' )
select Id, StaffId, ClockIn, ClockOut from @TimesheetEntries
-- Find all of the periods that need to be coalesced and start the process.
declare @Bar as Table ( Id Int Identity, StaffId Int, ClockIn DateTime, ClockOut DateTime )
insert into @Bar
select TSl.StaffId, TSl.ClockIn, TSr.ClockOut
from @TimesheetEntries as TSl inner join
-- The same staff member and the end of the left period is within two hours of the start of the right period.
@TimesheetEntries as TSr on TSr.StaffId = TSl.StaffId and DateDiff( ss, TSl.ClockOut, TSr.ClockIn ) between 0 and 7200
-- Continue coalescing periods until we run out of work.
declare @Changed as Bit = 1
while @Changed = 1
begin
set @Changed = 0
-- Coalesce periods.
update Bl
-- Take the later ClockOut time from the two rows.
set ClockOut = case when Br.ClockOut >= Bl.ClockOut then Br.ClockOut else Bl.ClockOut end
from @Bar as Bl inner join
@Bar as Br on Br.StaffId = Bl.StaffId and
-- The left row started before the right and either the right period is completely contained in the left or the right period starts within two hours of the end of the left.
Bl.ClockIn < Br.ClockIn and ( Br.ClockOut <= Bl.ClockOut or DateDiff( ss, Bl.ClockOut, Br.ClockIn ) < 7200 )
if @@RowCount > 0
set @Changed = 1
-- Delete rows where one period is completely contained in another.
delete Br
from @Bar as Bl inner join
@Bar as Br on Br.StaffId = Bl.StaffId and
( ( Bl.ClockIn < Br.ClockIn and Br.ClockOut <= Bl.ClockOut ) or ( Bl.ClockIn <= Br.ClockIn and Br.ClockOut < Bl.ClockOut ) )
if @@RowCount > 0
set @Changed = 1
end
-- Return all of the coalesced periods ...
select StaffId, ClockIn, ClockOut, 'Coalesced Periods' as [Type]
from @Bar
union all
-- ... and all of the independent periods.
select StaffId, ClockIn, ClockOut, 'Independent Period'
from @TimesheetEntries as TS
where not exists ( select 42 from @Bar where StaffId = TS.StaffId and ClockIn <= TS.ClockIn and TS.ClockOut <= ClockOut )
order by ClockIn, StaffId
我确信应该进行一些优化。