【问题标题】:How to get the total working hours for employees with SQL Server如何使用 SQL Server 获取员工的总工作时间
【发布时间】:2016-03-18 05:49:37
【问题描述】:

我有一个员工的考勤表,如下所示

ID     Employee-ID       Date      Coming-time  Leaving-time   Break(in hours)
1         1             2016-01-01   08:00:00     18:30:00       0,50 
2         1             2016-01-02   20:00:00     08:00:00       1,50
3         1             2016-01-03   18:30:00     06:00:00       1,50
4         1             2016-01-04   08:00:00     18:30:00       0,00

如何计算每个员工每个月的总工作时间而不休息.....

问题在于 DATEDIFF 在半小时内没有给我真正的价值,例如以下查询:

ID     Employee-ID       Month      TotalHours  WorkingTime   
1        1                12            10          9,5     
2        1                12            12          10,5     
3        1                12            12          10,5     
4        1                12            10          10   

你可以看到 TotalHours 是错误的,值应该是这样的

TotalHours
   10,5
   12
   11,5
   10,5

我正在尝试的查询是这样的:(感谢答案和cmets的帮助......)

(SELECT ID, Employee-ID, MONTH(Date) AS Month,
CASE
    WHEN Coming-time > Leaving-time THEN  DATEDIFF(HOUR, Leaving-time, Coming-time) 
    WHEN Coming-time <= Leaving-time THEN   DATEDIFF(HOUR, Coming-time, Leaving-time)  
END AS TotalHours,
CASE
    WHEN Coming-time > Leaving-time THEN  DATEDIFF(HOUR, Leaving-time, Coming-time) - Break 
    WHEN Coming-time <= Leaving-time THEN   DATEDIFF(HOUR, Coming-time, Leaving-time) - Break
END as WorkingTime
FROM      TblEmployee)

如何获得总小时数的真实值?

【问题讨论】:

  • 您注意到第 2 行和第 3 行中的到来时间大于离开时间吗?这带来了一个可能的缺陷……这些真的是在同一天,还是你认为如果离开少于来,那么第二天?更不用说这是否都是相同的员工 ID,那么您会及时报告一些有趣的事情
  • @hubson-bropa 你是真的,我的错误抱歉日期不同,感谢您的注意...
  • 每一列的数据类型是什么? break 中的逗号是本地化输出还是字符串列中的实际内容。
  • @adrianm Break 列类型是 Decimal,Coming-time 和 Leave-time 列类型是 Time,Month 列类型是 Date ...
  • Datediff 只查看小时值。 (即 datediff(小时,9:59,11:01)=> 11 - 9 = 2)。您需要做的是使用 datediff(minute, .. 并将结果除以 60)

标签: sql-server


【解决方案1】:

你曾经看过那张冰山海报,80% 都在水下,所以你不会考虑它。

你的问题很像。它看起来无伤大雅,但乍一看,却有一只隐藏的野兽。在这种情况下,那个野兽就是你必须这样做的数据结构,加上轮班似乎跨越几天的事实。短篇小说这里是你在 SQL 中的冰山解决方案。长话短说。

SELECT
    base.[Employee-ID],
    DATEPART(year, base.[Date]) AS [Year],
    DATEPART(month, base.[Date]) AS [Month],
    DATEPART(day, base.[Date]) AS [Day],
    SUM(base.HoursWorked) AS TotalHoursAtWork,
    SUM(base.HoursOnBreak) AS TotalBreakHours,
    SUM(base.HoursWorked) - SUM(base.HoursOnBreak) AS TotalHoursWorked
FROM
(
    SELECT
        [Employee-ID],
        CAST(StartDateTime AS DATE) [Date],
        CASE
            WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
                DATEDIFF(minute, StartDateTime, EndDateTime) / 60.0
            ELSE
                DATEDIFF(minute, StartDateTime, DATEADD(day, DATEDIFF(day, 0, StartDateTime), 1)) / 60.0
        END HoursWorked,
        CASE
            WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
                BreakHours
            ELSE
                BreakHours / 2
        END HoursOnBreak
    FROM (
        SELECT
            [ID],
            [Employee-ID],
            [Date],
            [Coming-time],
            [Leaving-time],
            [Break(in hours)] AS BreakHours,
            CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
            CASE
                WHEN [Coming-time] <= [Leaving-time] THEN 
                    CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
                ELSE
                    CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
            END AS EndDateTime
        FROM
            [sandbox].[dbo].[TblEmployee]
    ) AS firstDay
    UNION ALL
    SELECT
        [Employee-ID],
        CAST(EndDateTime AS DATE) [Date],
        CASE
            WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
                0
            ELSE
                DATEDIFF(minute, DATEADD(day, DATEDIFF(day, 0, EndDateTime), 0), EndDateTime) / 60.0
        END HoursWorked,
        CASE
            WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
                0
            ELSE
                BreakHours / 2
        END HoursOnBreak
    FROM (
        SELECT
            [ID],
            [Employee-ID],
            [Date],
            [Coming-time],
            [Leaving-time],
            [Break(in hours)] AS BreakHours,
            CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
            CASE
                WHEN [Coming-time] <= [Leaving-time] THEN 
                    CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
                ELSE
                    CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
            END AS EndDateTime
        FROM
            [sandbox].[dbo].[TblEmployee]
    ) AS secondDay
) AS base
GROUP BY
    base.[Employee-ID],
    DATEPART(year, base.[Date]),
    DATEPART(month, base.[Date]),
    DATEPART(day, base.[Date])

那么为什么会有这些东西呢?问题是日期和时间的时间报告存在根本缺陷。这实际上是在某个桶中工作的小时数,例如每天、每个任务、每个项目等。

由于您要查看每天/每月/每年的工作小时数,因此我们只需要获取这些数据并将其放入最低公分母中,即每天的小时数。

第 1 步 - 将传入和传出转换为日期时间格式。这让我们获得了相等的数据类型,顺便说一下,当即将到来的时间大于离开时会发生什么。

SELECT
    [ID],
    [Employee-ID],
    [Date],
    [Coming-time],
    [Leaving-time],
    [Break(in hours)],
    CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
    CASE
        WHEN [Coming-time] <= [Leaving-time] THEN 
            CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
        ELSE
            CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
    END AS EndDateTime
FROM
    [sandbox].[dbo].[TblEmployee]

第 2 步 - 现在我们已经获得了日期时间的真实表示,将其转换为每天的小时数。问题是班次有时会跨越几天。 ruh roh raggy。

所以让我们把它分开,在多天的shitfs中让每天休息一半。

警告:假设:

  • 如果离开时间是“之后”(小时:分钟超过),则假定离开时间是第二天的时间。
  • shiftft 永远不会超过 24 小时。如果某人早上 8 点进来,早上 10 点离开,则假定是 2 小时,而不是 26 小时。如果您没有离开的日期,这就是生活。
  • 休息并不总是一半一半,所以有些 那里的变化。

SELECT
    [Employee-ID],
    CAST(StartDateTime AS DATE) FirstDay,
    CASE
        WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
            DATEDIFF(minute, StartDateTime, EndDateTime) / 60.0
        ELSE
            DATEDIFF(minute, StartDateTime, DATEADD(day, DATEDIFF(day, 0, StartDateTime), 1)) / 60.0
    END FirstDayHours,
    CASE
        WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
            BreakHours
        ELSE
            BreakHours / 2
    END FirstDayBreak,
    CAST(EndDateTime AS DATE) SecondDay,
    CASE
        WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
            0
        ELSE
            DATEDIFF(minute, DATEADD(day, DATEDIFF(day, 0, EndDateTime), 0), EndDateTime) / 60.0
    END SecondDayHours,
    CASE
        WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
            0
        ELSE
            BreakHours / 2
    END SecondDayBreak
FROM (
    SELECT
        [ID],
        [Employee-ID],
        [Date],
        [Coming-time],
        [Leaving-time],
        [Break(in hours)] AS BreakHours,
        CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
        CASE
            WHEN [Coming-time] <= [Leaving-time] THEN 
                CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
            ELSE
                CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
        END AS EndDateTime
    FROM
        [sandbox].[dbo].[TblEmployee]
) AS base

第 3 步 - 问题是我们需要在一天的列中进行分组。下面的联合只是将第一天和第二天的逻辑分成两个单独的查询。高效 = 地狱不,功能 - 是的。

SELECT
    [Employee-ID],
    CAST(StartDateTime AS DATE) [Date],
    CASE
        WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
            DATEDIFF(minute, StartDateTime, EndDateTime) / 60.0
        ELSE
            DATEDIFF(minute, StartDateTime, DATEADD(day, DATEDIFF(day, 0, StartDateTime), 1)) / 60.0
    END HoursWorked,
    CASE
        WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
            BreakHours
        ELSE
            BreakHours / 2
    END HoursOnBreak
FROM (
    SELECT
        [ID],
        [Employee-ID],
        [Date],
        [Coming-time],
        [Leaving-time],
        [Break(in hours)] AS BreakHours,
        CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
        CASE
            WHEN [Coming-time] <= [Leaving-time] THEN 
                CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
            ELSE
                CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
        END AS EndDateTime
    FROM
        [sandbox].[dbo].[TblEmployee]
) AS firstDay
UNION ALL
SELECT
    [Employee-ID],
    CAST(EndDateTime AS DATE) [Date],
    CASE
        WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
            0
        ELSE
            DATEDIFF(minute, DATEADD(day, DATEDIFF(day, 0, EndDateTime), 0), EndDateTime) / 60.0
    END HoursWorked,
    CASE
        WHEN DATEDIFF(day, StartDateTime, EndDateTime) = 0 THEN
            0
        ELSE
            BreakHours / 2
    END HoursOnBreak
FROM (
    SELECT
        [ID],
        [Employee-ID],
        [Date],
        [Coming-time],
        [Leaving-time],
        [Break(in hours)] AS BreakHours,
        CAST([Date] AS SMALLDATETIME) + CAST([Coming-time] AS SMALLDATETIME) AS StartDateTime,
        CASE
            WHEN [Coming-time] <= [Leaving-time] THEN 
                CAST([Date] AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
            ELSE
                CAST(DATEADD(day, 1, [Date]) AS SMALLDATETIME) + CAST([Leaving-time] AS SMALLDATETIME)
        END AS EndDateTime
    FROM
        [sandbox].[dbo].[TblEmployee]
) AS secondDay

在此之后,您只需进行一些聚合和 whamo。想要按不同时间段进行聚合,只需更改分组方式以符合您的需要。

【讨论】:

    【解决方案2】:
    SELECT Employee-ID, MONTH, SUM(WorkingHour)
    FROM
    (
    SELECT  employee-ID,
            MONTH(date) as month,
            cast(DATEDIFF(HOUR,cast(coming-time as time),cast(leaving-time as time ))as float) -cast(break as float) as WorkingHour
    FROM TABLENAME
    )
    GROUP BY Employee-ID,MONTH
    ORDER BY Employee-ID,MONTH
    

    思路是这样的。

    【讨论】:

    • 感谢您的帮助,您的查询对我帮助很大,但即使我像您一样尝试使用 cast,我仍然得到错误的值,我怎样才能获得小时的真实值区别....
    • 转换为浮点数是关键,因为您需要一些浮点数。
    【解决方案3】:
    SELECT    [Employee-ID] as employee_id,
              MONTH(Date) as month,
              SUM(DATEDIFF(MINUTE, [Coming-time], [Leaving-time]) / 60 - [Break]) as working_time
    
    FROM      table_name
    
    GROUP BY  [Employee-ID],
              MONTH(Date)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-04
      • 2022-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多