1

使用 SQL Server 2008 R2,我们正在寻找一种方法来选择员工在夜间的轮班时间,在本例中为 22.00 和 6.00 +1。

我们的问题变成了如何获得班次跨越午夜的时间,或者我们如何获得班次从 05.30 到 22.30 开始并且在班次开始和结束时都有重叠时的重叠。

这是一个示例,这些是数据库中可用的数据以及我们正在寻找的结果:

        startDateTime     |       endDateTime         |   nightHours
--------------------------+---------------------------+----------------
 2012-07-04 05:00:00.000    2012-07-04 23:00:00.000          2 
 2012-07-04 18:00:00.000    2012-07-05 05:00:00.000          7

有没有人有一个例子或一些我们可以使用的好指针。

4

2 回答 2

2

这可能过于复杂,但它确实有效。我们使用许多 CTE 来构建有用的中间表示:

declare @Times table (
    ID int not null,
    StartTime datetime not null,
    EndTime datetime not null
)
insert into @Times (ID,StartTime,EndTime)
select 1,'2012-07-04T05:00:00.000','2012-07-04T23:00:00.000' union all
select 2,'2012-07-04T18:00:00.000','2012-07-05T05:00:00.000'

;With Start as (
    select MIN(DATEADD(day,DATEDIFF(day,0,StartTime),0)) as StartDay from @Times
), Ends as (
    select MAX(EndTime) EndTime from @Times
), Nights as (
    select DATEADD(hour,-2,StartDay) as NightStart,DATEADD(hour,6,StartDay) as NightEnd from Start
    union all
    select DATEADD(DAY,1,NightStart),DATEADD(DAY,1,NightEnd) from Nights n
    inner join Ends e on n.NightStart < e.EndTime
), Overlaps as (
    select
        t.ID,
        CASE WHEN n.NightStart > t.StartTime THEN n.NightStart ELSE t.StartTime END as StartPeriod,
        CASE WHEN n.NightEnd < t.EndTime THEN n.NightEnd ELSE t.EndTime END as EndPeriod
    from
        @Times t
            inner join
        Nights n
            on
                t.EndTime > n.NightStart and
                t.StartTime < n.NightEnd
), Totals as (
    select ID,SUM(DATEDIFF(hour,StartPeriod,EndPeriod)) as TotalHours
    from Overlaps
    group by ID
)
select
    *
from
    @Times t
        inner join
    Totals tot
        on
            t.ID = tot.ID

结果:

ID          StartTime               EndTime                 ID          TotalHours
----------- ----------------------- ----------------------- ----------- -----------
1           2012-07-04 05:00:00.000 2012-07-04 23:00:00.000 1           2
2           2012-07-04 18:00:00.000 2012-07-05 05:00:00.000 2           7

您会注意到我必须添加一ID列才能使我的相关性起作用。

StartCTE 找到最早的适用午夜。EndCTE 找到我们需要找到重叠夜晚的最后时间。然后,递归NightsCTE 计算这两个时间点之间的每晚。然后我们将其加入到原始表(in Overlaps)中,以找到每晚适用的那些时段。最后,在 中Totals,我们计算每个重叠时段贡献了多少小时。

这应该适用于多日活动。Totals如果您需要计算部分小时数,您可能希望将 CTE 更改为使用分钟,或应用一些其他舍入函数。

于 2012-07-04T09:29:54.593 回答
0

我认为,最好的方法是使用轮班开始时间和结束时间的函数。然后在函数内部有两种情况:第一种情况是在同一天开始和结束,另一种情况是在一天开始并在下一天结束。

对于在同一天开始和结束的情况

@TotalOvernightHours=0 

@AMDifference = Datediff(hh, @shiftStart, @6amOnThatDay);
if @AMDIfference > 0 than @TotalOvernightHours = @TotalOvernightHours + @AMDifference

@PMDifference Datediff(hh, @10pmOnThatDay, @ShiftEnd)
if @PMDifference > 0 than @TotalOvernightHours = @TotalOvernightHours + @PMDifference

对于开始和结束在不同日期的情况,假设它是 2 个班次:首先从 @ShiftStart 开始,但在午夜结束。第二个从午夜开始,在@ShiftEnd 结束。并且对于每个班次都应用上面的逻辑。

如果您的轮班时间超过 24 小时,请将它们分成更小的子班次,其中午夜是一个分隔符。因此,如果您有班次开始1 Jun 19:00和结束, 3 Jun 5:00那么您最终会得到三个子班次:

  • 6月1日 19:00 - 6月1日 24:00
  • 6月2日 00:00 - 6月2日 24:00
  • 6 月 3 日 00:00 - 6 月 3 日 5:00

对于每个子班次,您都会计算通宵时间。

我可能会编写一个函数来计算一个 24 小时期间的通宵时间,另一个函数将整个班次分成 24 小时块,然后将其输入第一个函数。

ps 这不是sql,只是伪代码。pps 这只有在你有能力创建函数时才有效。它会给你一个干净、易于准备的代码。

于 2012-07-04T09:39:08.407 回答