【问题标题】:Divide Monthly Working Hours into Weekly Hours - SQL Server将每月工作时间划分为每周工作时间 - SQL Server
【发布时间】:2017-05-28 18:17:16
【问题描述】:

我有一个要求,我必须使用以下每月数据为每个员工生成一份报告。该报告在每个月的第一天运行,并提供截至上个月第一天的数据。 要求是将这些数据划分为周数据。因此,如果每个月的第一天从“星期一”开始,则一周应该有 5 个工作日,“星期二”有 4 个工作日,“星期三”有 3 个工作日等等。并根据员工工作的天数计​​算工作时间对应的一周。如果每个月的周数都不同,那么报告应该相应地显示每周的数据。

EmpName Date        WorkTime
User1   2016-10-18  NULL
User1   2016-10-20  06:00:38
User1   2016-10-21  07:41:44
User1   2016-10-24  06:35:53
User1   2016-10-25  06:29:03
User1   2016-10-26  07:25:09
User1   2016-10-31  07:49:12
User1   2016-11-03  09:23:05
User1   2016-11-05  NULL
User1   2016-11-07  09:18:38
User1   2016-11-08  09:16:01
User1   2016-11-09  08:05:03
User1   2016-11-11  09:00:43
User1   2016-11-16  09:18:14

以下是上述查询的预期结果。

WeekNum WeekDur         EmpName Planned     Actual
Week1   18/10 - 22/10   User1   32:00:00    13:42:22
Week2   23/10 - 29/10   User1   40:00:00    20:30:05
Week3   30/10 - 31/10   User1   8:00:00     7:49:12

注意:计划小时数是根据工作日数计算的。表示周一至周五,因此每天 8 小时将提供 40 小时,每周 5 天。但是,需要计算所有 7 天的实际工作时间,以便如果有人在周末工作,那么实际工作时间可以相应地反映计划工作时间以外的任何额外时间。

注意:NULL 表示员工没有正确刷入/刷出。

抱歉,我自己并没有尝试过任何事情,因为我是这种要求的新手,并且在处理日期和时间查询方面经验很少。

我希望我已经提供了所有信息,如果有任何问题或困惑,请大家与我联系。

【问题讨论】:

    标签: sql-server datetime week-number weekday weekend


    【解决方案1】:

    我认为您实际上不能拥有超过 24 小时的 time 数据类型,因此我将您的工作时间分解为单独的字段,以便您做自己想做的事情。您可以通过将大日期 case 语句添加到您的 group by 中来取消 cte,但这意味着重复的代码,因此需要重复努力来更新它,我很懒惰:

    declare @t table (EmpName nvarchar(10), WorkDate date, WorkTime time);
    insert into @t values
     ('User1','20161018',NULL),('User1','20161020','06:00:38'),('User1','20161021','07:41:44'),('User1','20161024','06:35:53'),('User1','20161025','06:29:03'),('User1','20161026','07:25:09'),('User1','20161031','07:49:12'),('User1','20161103','09:23:05'),('User1','20161105',NULL),('User1','20161107','09:18:38'),('User1','20161108','09:16:01'),('User1','20161109','08:05:03'),('User1','20161111','09:00:43'),('User1','20161116','09:18:14');
    
    with cte as
    (
    select EmpName
                -- Pick the later of either the start of the current week or the current month.
            ,case when dateadd(wk, datediff(wk,0,WorkDate), 0) < dateadd(month,datediff(month,0,WorkDate),0)
                    then dateadd(month,datediff(month,0,WorkDate),0)    -- This calculates the start of the month.
                    else dateadd(wk, datediff(wk,0,WorkDate), 0)        -- This calculated the start of the week.
                    end as WeekStart
    
                -- Pick the earlier of either the end of the current week or the current month.
            ,case when dateadd(d,6,dateadd(wk, datediff(wk,0,WorkDate), 0)) >= dateadd(month,datediff(month,0,WorkDate)+1,0)
                    then dateadd(d,-1,dateadd(month,datediff(month,0,WorkDate)+1,0))    -- This calculates the last day of the month.
                    else dateadd(d,6,dateadd(wk, datediff(wk,0,WorkDate), 0))           -- This calculates the last day of the week.
                    end as WeekEnd
    
                -- Pick the earlier of either the friday of the current week or or the end of the current month.
            ,case when dateadd(d,4,dateadd(wk, datediff(wk,0,WorkDate), 0)) > case when dateadd(d,6,dateadd(wk, datediff(wk,0,WorkDate), 0)) >= dateadd(month,datediff(month,0,WorkDate)+1,0)
                                                                                    then dateadd(d,-1,dateadd(month,datediff(month,0,WorkDate)+1,0))    -- This calculates the last day of the month.
                                                                                    else dateadd(d,6,dateadd(wk, datediff(wk,0,WorkDate), 0))           -- This calculates the last day of the week.
                                                                                    end
    
                    then case when dateadd(d,6,dateadd(wk, datediff(wk,0,WorkDate), 0)) >= dateadd(month,datediff(month,0,WorkDate)+1,0)
                                then dateadd(d,-1,dateadd(month,datediff(month,0,WorkDate)+1,0))    -- This calculates the last day of the month.
                                else dateadd(d,6,dateadd(wk, datediff(wk,0,WorkDate), 0))           -- This calculates the last day of the week.
                                end
                    else dateadd(d,4,dateadd(wk, datediff(wk,0,WorkDate), 0))           -- This calculates the Friday of the week.
                    end as WorkingWeekEnd
    
            ,datepart(hour,WorkTime) as HoursWorked
            ,datepart(minute,WorkTime) as MinutesWorked
            ,datepart(second,WorkTime) as SecondsWorked
    from @t
    )
    select EmpName
            ,WeekStart
            ,WeekEnd
            ,WorkingWeekEnd
            ,avg(datediff(d,WeekStart,WorkingWeekEnd)+1) * 8 as PlannedHoursWorked
            ,isnull(sum(HoursWorked),0) as HoursWorked
            ,isnull(sum(MinutesWorked),0) as MinutesWorked
            ,isnull(sum(SecondsWorked),0) as SecondsWorked
    from cte
    group by EmpName
            ,WeekStart
            ,WeekEnd
            ,WorkingWeekEnd
    order by EmpName
            ,WeekStart;
    

    输出:

    ╔═════════╦═════════════════════════╦═════════════════════════╦═════════════════════════╦════════════════════╦═════════════╦═══════════════╦═══════════════╗
    ║ EmpName ║        WeekStart        ║         WeekEnd         ║     WorkingWeekEnd      ║ PlannedHoursWorked ║ HoursWorked ║ MinutesWorked ║ SecondsWorked ║
    ╠═════════╬═════════════════════════╬═════════════════════════╬═════════════════════════╬════════════════════╬═════════════╬═══════════════╬═══════════════╣
    ║ User1   ║ 2016-10-17 00:00:00.000 ║ 2016-10-23 00:00:00.000 ║ 2016-10-21 00:00:00.000 ║                 40 ║          13 ║            41 ║            82 ║
    ║ User1   ║ 2016-10-24 00:00:00.000 ║ 2016-10-30 00:00:00.000 ║ 2016-10-28 00:00:00.000 ║                 40 ║          19 ║            89 ║            65 ║
    ║ User1   ║ 2016-10-31 00:00:00.000 ║ 2016-10-31 00:00:00.000 ║ 2016-10-31 00:00:00.000 ║                  8 ║           7 ║            49 ║            12 ║
    ║ User1   ║ 2016-11-01 00:00:00.000 ║ 2016-11-06 00:00:00.000 ║ 2016-11-04 00:00:00.000 ║                 32 ║           9 ║            23 ║             5 ║
    ║ User1   ║ 2016-11-07 00:00:00.000 ║ 2016-11-13 00:00:00.000 ║ 2016-11-11 00:00:00.000 ║                 40 ║          35 ║            39 ║            85 ║
    ║ User1   ║ 2016-11-14 00:00:00.000 ║ 2016-11-20 00:00:00.000 ║ 2016-11-18 00:00:00.000 ║                 40 ║           9 ║            18 ║            14 ║
    ╚═════════╩═════════════════════════╩═════════════════════════╩═════════════════════════╩════════════════════╩═════════════╩═══════════════╩═══════════════╝
    

    【讨论】:

    • 感谢您的回复,但根据场景,上述内容是正确的。如果您可以看到第一行,则在 10 月 17 日和 10 月 23 日之间,我们有 5 个工作日(周一至周五),HoursPlanned 应该是 40(count(1)*8),但结果显示 24 小时,即 3 天只要。我正在寻找一种解决方案,它可以帮助我计算从每月 1 日开始的每周小时数,而不管该日期的哪一天。我希望这将有助于更多地了解需求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 1970-01-01
    • 2021-10-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多