【问题标题】:Calculating OT based on week/year between two dates根据两个日期之间的周/年计算 OT
【发布时间】:2019-09-20 17:46:37
【问题描述】:

我们每月支付两次。支付期为 1-15 日和 16-EOM。因此,支付期的结束可以在任何一天结束。

我们在周一至周日支付超过 40 小时的加班费。

如果工资期在星期六结束并且员工有 47 小时,我们会在该支票上支付 7 小时。如果员工在星期天工作,而现在一周的总小时数是 52..我们将支付 5 小时他们的下一次检查.. 现在是手动计算过程。

我正在努力思考如何编写查询以使我获得额外的结转……

这是我上一个支付周期 2019 年 9 月 1 日至 2019 年 9 月 15 日的每日工作总量的输出,因为第一个是星期天。我需要从 8 开始计算一周的加班时间/ 2019 年 2 月 26 日。在这个特殊的员工身上,他周末随叫随到。在 2019 年 8 月 16 日至 2019 年 8 月 31 日期间,他获得了 6.28 小时的加班费,在 2019 年 9 月 1 日加班了两个小时,因此 2 小时的加班需要结转到 9 月 1 日/ 2019 年至 2019 年 9 月 15 日检查。

ID HRS WK CDATE STU02 8.16 35 2019-08-26 00:00:00.000 STU02 9.37 35 2019-08-27 00:00:00.000 STU02 9.07 35 2019-08-28 00:00:00.000 STU02 7.91 35 2019-08-29 00:00:00.000 STU02 9.12 35 2019-08-30 00:00:00.000 STU02 2.65 35 2019-08-31 00:00:00.000 STU02 2.00 35 2019-09-01 00:00:00.000 STU02 4.17 36 2019-09-02 00:00:00.000 STU02 9.40 36 2019-09-03 00:00:00.000 STU02 8.80 36 2019-09-04 00:00:00.000 STU02 8.90 36 2019-09-05 00:00:00.000 STU02 8.93 36 2019-09-06 00:00:00.000 STU02 2.56 36 2019-09-07 00:00:00.000 STU02 2.00 36 2019-09-08 00:00:00.000 STU02 8.66 37 2019-09-09 00:00:00.000 STU02 9.14 37 2019-09-10 00:00:00.000 STU02 9.07 37 2019-09-11 00:00:00.000 STU02 9.29 37 2019-09-12 00:00:00.000 STU02 9.94 37 2019-09-13 00:00:00.000 STU02 2.00 37 2019-09-15 00:00:00.000

我很感激这个人对我尝试过的许多不同的事情的帮助。

** 使用表和数据更新 **

/****** Object:  Table [dbo].[DLI_TEST_DATE]    Script Date: 9/18/2019 3:50:50 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[DLI_TEST_DATE](
    [EMPLOYEE_ID] [nvarchar](15) NULL,
    [REG_TOTAL] [float] NULL,
    [WEEK_NUM] [int] NULL,
    [CDATE] [datetime] NULL,
    [DAYOFWK] [int] NULL
) ON [PRIMARY]

GO

INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 2, 35, CAST(N'2019-08-25 00:00:00.000' AS DateTime), 1)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 8.16, 35, CAST(N'2019-08-26 00:00:00.000' AS DateTime), 2)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 9.37, 35, CAST(N'2019-08-27 00:00:00.000' AS DateTime), 3)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 9.07, 35, CAST(N'2019-08-28 00:00:00.000' AS DateTime), 4)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 7.91, 35, CAST(N'2019-08-29 00:00:00.000' AS DateTime), 5)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 9.12, 35, CAST(N'2019-08-30 00:00:00.000' AS DateTime), 6)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 2.65, 35, CAST(N'2019-08-31 00:00:00.000' AS DateTime), 7)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 2, 36, CAST(N'2019-09-01 00:00:00.000' AS DateTime), 1)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 4.17, 36, CAST(N'2019-09-02 00:00:00.000' AS DateTime), 2)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 9.4, 36, CAST(N'2019-09-03 00:00:00.000' AS DateTime), 3)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 8.8, 36, CAST(N'2019-09-04 00:00:00.000' AS DateTime), 4)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 8.9, 36, CAST(N'2019-09-05 00:00:00.000' AS DateTime), 5)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 8.93, 36, CAST(N'2019-09-06 00:00:00.000' AS DateTime), 6)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 2.56, 36, CAST(N'2019-09-07 00:00:00.000' AS DateTime), 7)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 2, 37, CAST(N'2019-09-08 00:00:00.000' AS DateTime), 1)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 8.66, 37, CAST(N'2019-09-09 00:00:00.000' AS DateTime), 2)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 9.14, 37, CAST(N'2019-09-10 00:00:00.000' AS DateTime), 3)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 9.07, 37, CAST(N'2019-09-11 00:00:00.000' AS DateTime), 4)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 9.29, 37, CAST(N'2019-09-12 00:00:00.000' AS DateTime), 5)
GO
INSERT [dbo].[DLI_TEST_DATE] ([EMPLOYEE_ID], [REG_TOTAL], [WEEK_NUM], [CDATE], [DAYOFWK]) VALUES (N'STU02', 9.94, 37, CAST(N'2019-09-13 00:00:00.000' AS DateTime), 6)
GO

【问题讨论】:

  • 您使用哪种 RDBMS?
  • 您的数据创建脚本将 2019-09-01 作为第 36 周,但我认为应该是第 35 周。与 2019-09-08 类似。
  • MS SQL 2012 用于我的数据库。
  • 两件事。首先,您的公司是否严格在白天工作,或者是否有可能轮班跨越午夜的界限?其次,解决此问题的唯一可能的系统方法是将每周视为临时计算,然后在接下来的工资单中进行平衡。

标签: sql sql-server


【解决方案1】:

如果适用,子查询将给定一周的小时数分成 2 个不同的支付期。 where 条款将仅包括加班时间,并且分为两个支付期。 选择包括加班的数学。

declare @DLI_TEST_DATA TABLE (
    [EMPLOYEE_ID] [nvarchar](15) NULL,
    [REG_TOTAL] [float] NULL,
    [WEEK_NUM] [int] NULL,
    [CDATE] [datetime] NULL,
    [DAYOFWK] [int] NULL
) 

INSERT into @DLI_TEST_DATA
    VALUES (N'STU02', 2, 34, CAST(N'2019-08-25 00:00:00.000' AS DateTime), 1)
        ,(N'STU02', 8.16, 35, CAST(N'2019-08-26 00:00:00.000' AS DateTime), 2)
        ,(N'STU02', 9.37, 35, CAST(N'2019-08-27 00:00:00.000' AS DateTime), 3)
        ,(N'STU02', 9.07, 35, CAST(N'2019-08-28 00:00:00.000' AS DateTime), 4)
        ,(N'STU02', 7.91, 35, CAST(N'2019-08-29 00:00:00.000' AS DateTime), 5)
        ,(N'STU02', 9.12, 35, CAST(N'2019-08-30 00:00:00.000' AS DateTime), 6)
        ,(N'STU02', 2.65, 35, CAST(N'2019-08-31 00:00:00.000' AS DateTime), 7)
        ,(N'STU02', 2, 35, CAST(N'2019-09-01 00:00:00.000' AS DateTime), 1)
        ,(N'STU02', 4.17, 36, CAST(N'2019-09-02 00:00:00.000' AS DateTime), 2)
        ,(N'STU02', 9.4, 36, CAST(N'2019-09-03 00:00:00.000' AS DateTime), 3)
        ,(N'STU02', 8.8, 36, CAST(N'2019-09-04 00:00:00.000' AS DateTime), 4)
        ,(N'STU02', 8.9, 36, CAST(N'2019-09-05 00:00:00.000' AS DateTime), 5)
        ,(N'STU02', 8.93, 36, CAST(N'2019-09-06 00:00:00.000' AS DateTime), 6)
        ,(N'STU02', 2.56, 36, CAST(N'2019-09-07 00:00:00.000' AS DateTime), 7)
        ,(N'STU02', 2, 36, CAST(N'2019-09-08 00:00:00.000' AS DateTime), 1)
        ,(N'STU02', 8.66, 37, CAST(N'2019-09-09 00:00:00.000' AS DateTime), 2)
        ,(N'STU02', 9.14, 37, CAST(N'2019-09-10 00:00:00.000' AS DateTime), 3)
        ,(N'STU02', 9.07, 37, CAST(N'2019-09-11 00:00:00.000' AS DateTime), 4)
        ,(N'STU02', 9.29, 37, CAST(N'2019-09-12 00:00:00.000' AS DateTime), 5)
        ,(N'STU02', 9.94, 37, CAST(N'2019-09-13 00:00:00.000' AS DateTime), 6)

select WEEK_NUM,fullweek - (case when endfirstperiod+endsecondperiod <= 40 then 40.0 else endfirstperiod+endsecondperiod end) OvertimeCarriedOver
from (
    select week_num,sum(reg_total) fullweek
        ,sum(case when day(cdate) between 10 and 15 then reg_total else 0 end) endfirstperiod
        ,sum(case when day(cdate) between 16 and 21 then reg_total else 0 end) beginsecondperiod
        ,sum(case when day(cdate) between day(eomonth(cdate)) - 5 and day(eomonth(cdate)) then reg_total else 0 end) endsecondperiod
        ,sum(case when day(cdate) between 1 and 6 then reg_total else 0 end) beginfirstperiod
    from @DLI_TEST_DATA
    group by week_num
) basic
where fullweek > 40.0
    and beginfirstperiod+beginsecondperiod > 0
    and endfirstperiod+endsecondperiod > 0
order by week_num

【讨论】:

    【解决方案2】:

    核心思想是将所有内容按逻辑周分组,然后查看相关支付期与该周第一天不匹配的日期。

    carryover_max 是那些日子的小时数。根据第 40 小时的工作时间,该数字可能过高。在最终输出中,它以该周的总加班时间为上限。

    with weeks as (
        select week_start, week_end,
            case when sum(REG_TOTAL) > 40
                 then sum(REG_TOTAL) - 40 else 0 end as overtime_total,
            case when sum(REG_TOTAL) > 40
                 then sum(case when period <> week_period then REG_TOTAL else 0 end)
                 else 0 end carryover_max
        from
            dbo.DLI_TEST_DATE cross apply (
                select
                    dateadd(day, -datepart(weekday, dateadd(day, -1, cdate)) + 1, cdate)
            ) as v(week_start) cross apply (
                select
                    dateadd(day, 6, week_start),
                    case when datepart(day, week_start) <= 15 then 1 else 2 end,
                    case when datepart(day, cdate) <= 15 then 1 else 2 end
            ) as v2(week_end, week_period, period)
        group by week_start, week_end
    ), periods as (
        select distinct period_start, period_end
        from
            dbo.DLI_TEST_DATE cross apply (
               select
                    datefromparts(datepart(year, cdate), datepart(month, cdate),
                        case when datepart(day, cdate) <= 15 then 1 else 15 end),
                    datefromparts(datepart(year, cdate), datepart(month, cdate),
                        case when datepart(day, cdate) <= 15 then 16 else datepart(day, eomonth(cdate)) end)
            ) v(period_start, period_end)
    )
    select period_start,
        sum(case when carryover_max > overtime_total then overtime_total else carryover_max end) as overtime_owed
    from
        periods inner join
            weeks w on w.week_end >= period_start and w.week_end <= period_end
    group by period_start;
    

    https://rextester.com/TVXF30798

    顺便说一句,您真的不想使用float 作为您的数据类型。

    由于有关于周编号的问题,我刚刚计算了日期。无论如何,这在新年的几周内应该会更好。我还假设您的服务器设置在使用 datepart(weekday...) 时将星期日作为一周的第一天。

    【讨论】:

    • 我强烈建议像这样的工资单问题,系统的设计原理与普通会计相似,输入工作时间会立即导致“债务”应用于工资账户,并且工资单的处理导致平衡“信用”应用于当前债务。这将使支付的支付与支付的计算断开。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-06
    • 1970-01-01
    • 1970-01-01
    • 2011-04-17
    • 2020-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多