【问题标题】:How to calculate employees working hours in sql如何在sql中计算员工的工作时间
【发布时间】:2020-10-01 07:10:30
【问题描述】:

我公司的营业时间是 7:30 到 18 点,13:00 到 15:00 是午餐时间。 重要的部分是关于午餐时间,根本不应该算作员工的工作时间。

  • 因此假设员工在 8:30 开始工作并在 15:00 离开,因此应该为他计算 4:30 小时的时间。实际上我想在不同情况下从员工的出勤时间中删除午餐时间:

我的字段在Sql中都是Time(7)格式的。

【问题讨论】:

  • 到目前为止,您尝试了什么?你在哪里卡住了?
  • @SelimYildiz 我计算 datediff(enter-exit)-datediff(13:00,15:00) 但我需要从工作时间中删除午餐时间的重叠时间
  • 你能分享你为解决这个问题而编写的代码吗?您面临的问题是什么?
  • @ChetanRanpariya 我需要代码从出勤时间中删除午餐时间以计算工作时间

标签: c# sql winforms time sql-server-2017


【解决方案1】:

这是 SQL Server 中的一种方法。它从根本上将每个人的轮班时间分为两部分——午餐前和午餐后。当轮班进入(或过去)午餐时,它使用午餐时间作为界限。

我还用位(例如,CTE、子查询等)编写了它,因此您可以单独运行它们以查看它们的作用。您可能需要为自己的数据库结构等更新它。

-- Data setup

CREATE TABLE #WorkLog (WorkDate date, StartTime time, EndTime time, StartLunch time, EndLunch time)

INSERT INTO #WorkLog (WorkDate, StartTime, EndTime, StartLunch, EndLunch) VALUES
('2020-09-01', '07:30', '18:00', '13:00', '15:00'),
('2020-09-02', '12:00', '15:00', '13:00', '15:00'),
('2020-09-03', '15:00', '18:00', '13:00', '15:00'),
('2020-09-04', '08:30', '15:00', '13:00', '15:00')

SELECT * FROM #WorkLog

------

-- Find times worked

; WITH PreLunchTimes AS
        (SELECT WorkDate,
                StartTime AS StartTime,
                CASE WHEN EndTime < StartLunch THEN EndTime ELSE StartLunch END AS EndTime
        FROM    #WorkLog
        WHERE   StartTime < StartLunch
        ),
    PostLunchTimes AS
        (SELECT WorkDate,
                CASE WHEN StartTime > EndLunch THEN StartTime ELSE EndLunch END AS StartTime,
                EndTime AS EndTime
        FROM    #WorkLog
        WHERE   EndTime > EndLunch
        )
SELECT  WorkDate, SUM(Elapsed_Mins) AS Total_Work_Mins, CAST(DATEADD(minute, SUM(Elapsed_Mins), 0) AS time) AS Total_work_time
FROM   (SELECT  WorkDate, DATEDIFF(minute, StartTime, EndTime) AS Elapsed_Mins
        FROM    PreLunchTimes
            UNION ALL
        SELECT  WorkDate, DATEDIFF(minute, StartTime, EndTime) AS Elapsed_Mins
        FROM    PostLunchTimes
        ) AS A
GROUP BY WorkDate
ORDER BY WorkDate

这是db<>fiddle

问题:

  • 如果您的班次超过午夜,则需要添加适当的代码来处理。
  • 如果所有午餐时间都是 13:00 到 15:00,那么您可以将它们设置为变量(例如,@LunchStart 和 @LunchEnd),而不是将它们存储在数据中。

【讨论】:

    【解决方案2】:

    我发布此代码来解决您的问题

    TimeSpan? ActualTime = obj.ExitTime - obj.EnterTime;
    string[] LunchTime = obj.lunchTimeString.Split('-');
    TimeSpan ActualLunchTime = TimeSpan.Parse(LunchTime[1].Trim()) - TimeSpan.Parse(LunchTime[0].Trim());
    TimeSpan WorkingHours = TimeSpan.Parse(ActualTime.ToString().Trim()) - TimeSpan.Parse(ActualLunchTime.ToString().Trim());
    

    在这段代码中我所做的是:

    1. 您必须计算包括午餐时间在内的实际时间。
    2. 您的第二个问题是午餐时间,它的格式是 (13:00 - 15:00)。所以,我不能在 sql 中占用 time(7),所以将其作为 varchar,然后用 '-' 拆分。
    3. 然后用它计算总的午餐时间。
    4. 最后减去实际时间和实际午餐时间,得到你的工作时间。

    【讨论】:

      【解决方案3】:

      唉,你忘了给我们你的数据库表的结构。您的 EnterTime 和 ExitTime 是完整的 DateTimes,还是您有 WorkDay,以及员工开始和停止工作的那一天的 TimeOfDay?

      除了你忘了给我们你的表结构外,我不知道你是如何与你的数据库通信的,是使用实体框架,还是说传统的 SQL,你说的是哪种 SQL 方言。有些方言可以进行 DateTime 计算,有些则没有。

      MadReflection 的评论很明智:如果您可以更改数据库,请考虑记住午餐前班次的开始和停止日期时间以及午餐后班次的开始/停止日期时间。这样,您就可以为未来的变化做好准备,例如每位员工的不同午餐时间,甚至是员工每天的不同午餐时间。如果您在 DateTime 中考虑进入和退出,您甚至可以让员工在除夕开始工作并在不同的年份停止工作。

      幸运的是,您不必让数据库计算午餐时间,因为您可以在本地计算午餐时间。这比让数据库管理系统计算它们并将结果从 DBMS 传输到本地进程要快。

      在本地计算午餐时间只需减去两个 long,这比传输数据所需的代码要快得多。

      所以我们不能给你 SQL。您必须自己编写获取数据的过程。

      您需要一种方法来获取一段时间内每个员工的 EnterTime / ExitTime ,或者可能是所有员工的。结果是这样的:

      class WorkingTimes
      {
           public EmployeeInformation Employee {get; set;}
      
           public DateTime EnterTime {get; set;}
           public DateTime ExitTime {get; set;}
      }
      

      以及为每个员工或所有员工获取此信息的方法:

      IEnumerable<WorkingTimes> FetchWorkingTimes(DateTime beginPeriod, DateTime endPeriod)
      {
          ...
      }
      
      IEnumerable<WorkingTimes> FetchWorkingTimes(int employeeId, 
           DateTime beginPeriod, DateTime endPeriod)
      {
          ...
      }
      

      要将获取的数据转换为您想要的格式,可以使用简单的 LINQ 方法:

      class WorkingTimeExt
      {
          // TODO: if needed: add employee information
      
          public System.DayOfWeek DayOfWeek => this.EnterTime.DayOfWeek;
          public DateTime EnterTime {get; set;}
          public DateTime ExitTime {get; set;}
      
          public DateTime StartLunch {get; set;}
          public DateTime StopLunch {get; set;}
          public string Lunch => String.Format("...", this.StartLunch, this.StopLunch);
      
          public TimeSpan WorkingTime => this.StartLunch - this.EnterTime
                                       + this.ExitTime - this.StopLunch;
      }
      

      WorkingTime的计算速度很快,就是一些long的加法。

      TimeSpan defaultLunchStart = TimeSpan.FromHours(13.00);
      TimeSpan defaultLunchEnd = TimeSpan.FromHours(15.00);
      
      IEnumerable<WorkingTimes> fetchedWorkingTimes = ...
      IEnumerable<WorkingTimesExt> result = fetchedWorkingTimes
          .Select(fetchedTimes => new WorkingTimesExt
          {
               EnterTime = fetchedWorkingTimes.EnterTime,
               ExitTime = fetchedWorkingTimes.ExitTime,
               StartLunchTime = fetchedWorkingTime.EnterTime.Date + defaultLunchStart,
               StopLunchTime = fetchedWorkingTime.ExitTime.Data + defaultLunchStop,
          });
      

      如果您需要定期执行此操作,请考虑为其创建扩展方法:

      IEnumerable<WorkingTimesExt> result = ToExtendedWorkingTimes(
           this IEnumerable<WorkingTimes> source,
           TimeSpan timeLunchStart,
           TimeSpan timeLunchStop)
      {
          return source.Select(fetchedTimes => new WorkingTimesExt
          {
               EnterTime = fetchedWorkingTimes.EnterTime,
               ExitTime = fetchedWorkingTimes.ExitTime,
               StartLunchTime = fetchedWorkingTime.EnterTime.Date + timeLunchStart,
               StopLunchTime = fetchedWorkingTime.ExitTime.Data + timeLunchStop,
          });
      }
      

      【讨论】:

        【解决方案4】:

        另一个工作时间转换为 HH:MM 格式的工作选项:

        CREATE TABLE t1 (dow VARCHAR(10), workstart TIME, workend TIME);
        INSERT INTO t1 VALUES
        ('Monday', '7:30', '18:00'),
        ('Tuesday', '12:00', '15:00'),
        ('Wednesday', '15:00', '18:00'),
        ('Thursday', '8:30', '15:00');
        
        WITH 
        lunch(lunchstart, lunchend) AS (
        SELECT CAST('13:00' AS TIME), CAST('15:00' AS TIME)
        ),
        attendances AS (
        SELECT *,
        CASE WHEN workstart <= lunchstart THEN
             CASE WHEN workend <= lunchstart THEN
                  DATEDIFF(minute, workstart, workend)
             ELSE
                  DATEDIFF(minute, workstart, lunchstart)
             END
        ELSE 0 END --AS morning_shift
        +
        CASE WHEN workend >= lunchend THEN
             CASE WHEN workstart >= lunchend THEN
                  DATEDIFF(minute, workstart, workend)
             ELSE
                  DATEDIFF(minute, lunchend, workend)
             END
        ELSE 0 END --AS afternoon_shift 
        AS total_mins 
        FROM t1, lunch
        )
        SELECT *, FORMAT(total_mins / 60 * 100 + total_mins % 60, '#:0#') AS hhmm 
        FROM attendances;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-08-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多