SQL 表示员工签到和签退的查询 - 白天和晚上的工作时间

SQL Query to represent employee check-in and check-out - Working Hours in a day and nights

我table记录为

id              | int
badge_id        | varchar
status          | enum('check_in','check_out')
timestamp       | datetime

数据库有数据为

id  badge_id         status          Timestamp

1   EMP0001         check_in        2021-11-22 08:00:00
2   EMP0002         check_in        2021-11-22 08:00:50
3   EMP0001         check_out       2021-11-22 13:00:00
4   EMP0002         check_out       2021-11-22 13:01:00
5   EMP0001         check_in        2021-11-22 13:31:00
6   EMP0002         check_in        2021-11-22 13:33:50
7   EMP0001         check_out       2021-11-22 18:03:00
8   EMP0002         check_out       2021-11-22 18:04:00
9   EMP0003         check_in        2021-11-22 17:00:00
10  EMP0004         check_in        2021-11-22 19:00:50
11  EMP0003         check_out       2021-11-22 21:30:00
12  EMP0004         check_out       2021-11-23 00:22:00
13  EMP0003         check_in        2021-11-22 22:01:00
14  EMP0004         check_in        2021-11-23 00:55:50
15  EMP0003         check_out       2021-11-23 03:30:00
16  EMP0004         check_out       2021-11-23 05:38:00

白班和夜班(第二天结束)

我正在努力让他们每个人的工作时间都适合白班,但现在也可以在夜班工作。有什么建议吗?

要获取的代码:

SELECT
    id,
    MIN(timestamp) AS check_in_at,
    MAX(timestamp) AS check_out_at,
    TIMESTAMPDIFF(MINUTE, MIN(timestamp), MAX(timestamp))/60 AS total_time
FROM cte where timestamp BETWEEN '2021-12-27' AND '2021-12-30'
GROUP BY
    badge_id,date(timestamp)
ORDER BY
    badge_id,timestamp;

预期结果:

badge_id    Check_in_at     Check_out_at        Total_time

EMP0001     2021-11-22 08:00:00 2021-11-22 18:03:00 7.9500
EMP0002     2021-11-22 08:00:50 2021-11-22 18:04:00 7.9833
EMP0003     2021-11-22 17:00:00 2021-11-23 03:30:00 10.3000
EMP0004     2021-11-22 19:00:00 2021-11-23 05:38:00 10.3800

有人可以建议我查询吗?

我无法完全匹配您的预期输出,因为我不明白(例如)2021-11-22 08:00:00 2021-11-22 18:03:00 是 7.95 小时,而根据我的计算是 10.05 小时。此外,白天有一个 check_in/out 完成,因此有 2 种可能的计算,一种排除休息时间,另一种忽略休息时间。

请注意,我已选择在第一个 cte 中使用 correlated subqueries 以在 1 天的时间间隔内将最早签到与最新签出对齐,从而使最终结果允许跨越午夜的班次。另请注意,限制数据日期范围的 where 子句应出现在第一个 cte 中。

with cte as (
    select distinct
          t.badge_id
        , (select min(s.timestamp) 
           from mytable s
           where t.badge_id = s.badge_id and t.timestamp > date_sub(s.timestamp,interval 1 day)
          ) check_in_at
        , (select max(s.timestamp) 
           from mytable s
           where t.badge_id = s.badge_id and t.timestamp < date_add(s.timestamp,interval 1 day)
          ) check_out_at
    from mytable t
    /* where t.timestamp >= '2021-12-27' and t.timestamp < '2022-01-01' */
    )
select
      badge_id
    , check_in_at
    , check_out_at
    , TIMESTAMPDIFF(MINUTE, check_in_at, check_out_at)/60 AS total_time
from cte
order by
      badge_id
    , check_in_at

结果(来自示例数据)是:

+----------+---------------------+---------------------+------------+
| badge_id |     check_in_at     |    check_out_at     | total_time |
+----------+---------------------+---------------------+------------+
| EMP0001  | 2021-11-22 08:00:00 | 2021-11-22 18:03:00 |    10.0500 |
| EMP0002  | 2021-11-22 08:00:50 | 2021-11-22 18:04:00 |    10.0500 |
| EMP0003  | 2021-11-22 17:00:00 | 2021-11-23 03:30:00 |    10.5000 |
| EMP0004  | 2021-11-22 19:00:50 | 2021-11-23 05:38:00 |    10.6167 |
+----------+---------------------+---------------------+------------+

db<>fiddle here

上一个请求的查询(产生不同的结果):

with cte1 as (
    select
          t.id
        , t.badge_id
        , t.timestamp as check_in_at
        , (select o.timestamp from mytable as o
           where t.status = 'check_in' and o.status = 'check_out'
           and o.badge_id = t.badge_id
           and o.timestamp > t.timestamp
           order by timestamp
           limit 1)
          as check_out_at
    from mytable t
    /* where timestamp BETWEEN '2021-12-27' AND '2021-12-30' */
    )
, cte as (
    select
        *
        , TIMESTAMPDIFF(MINUTE, check_in_at, check_out_at)/60 AS total_time
    from cte1
    where check_out_at is not null
    )
SELECT
  badge_id
, date(check_in_at) AS date_check_in
, MIN(check_in_at) AS check_in_at
, MAX(check_out_at) AS check_out_at
, SUM(total_time) as total_time_with_break
, TIMESTAMPDIFF(MINUTE, MIN(check_in_at), MAX(check_out_at))/60 AS total_time_no_break
FROM cte 
GROUP BY
    badge_id, date(check_in_at)
ORDER BY
    badge_id, date(check_in_at)