计算工作时间的两行之间的时间差

Posted

技术标签:

【中文标题】计算工作时间的两行之间的时间差【英文标题】:Calculate the time difference between two rows for work time 【发布时间】:2019-01-11 14:26:16 【问题描述】:

我有一张桌子:

timedate                 workclock
2018-01-01 09:00:00      check-in
2018-01-01 12:30:40      check-in
2018-01-02 09:00:00      check-in
2018-01-02 11:29:00      check-out
2018-01-03 14:29:00      check-out

我需要输出如下所示:

date          checkin      checkout     working-time
2018-01-01    09:00        none         missing checkout
2018-01-02    09:00        11:29        02:29
2018-01-03    none         14:29        missing checkin

我无法加入行,因此我们将不胜感激 还有第三个 col 代表工人标签,但这应该很容易分组。

【问题讨论】:

该表是否有any 方法来识别哪个签入匹配哪个签出?也许是签入或签出的人/物品的一些 ID?否则,计算的时间将是错误的,因为这些行不会与相应的签入/签出相关联。 @Wrokar 我相信这是日期。我也相信 OP 在他们的 Desired Results/Output 中有一个错字,最后一条记录应该在 2018-01-03 之前(也许是为了消除一些混乱?) 您是否缺少2018-01-01 12:30:40 none missing checkout 对不起,最后请求的结果有错字,应该是 2018-01-03 作为输入数据。我只是修复它。 【参考方案1】:

使用视图可以简化查询,所以使用类似

create view in_gate_times as
select date(date_time) xdate, time(date_time) xtime
  from gate_times where gate_op = 'check-in';

create view out_gate_times as
select date(date_time) xdate, time(date_time) xtime
  from gate_times where gate_op = 'check-out';

然后使用以下查询

select i.xdate, i.xtime, ifnull(o.xtime, 'missing')
  from in_gate_times i
  left join out_gate_times o on i.xdate = o.xdate
 union
select o.xdate, ifnull(i.xtime, 'missing'), o.xtime
  from in_gate_times i
 right join out_gate_times o on i.xdate = o.xdate
 order by 1, 2, 3

查看SQLFiddle

如果使用union 会使您的查询变慢,请使用union all 并进行以下更改

select i.xdate, i.xtime, ifnull(o.xtime, 'missing')
  from in_gate_times i
  left join out_gate_times o on i.xdate = o.xdate
 union all
select o.xdate, ifnull(i.xtime, 'missing'), o.xtime
  from in_gate_times i
 right join out_gate_times o on i.xdate = o.xdate
 where i.xdate is null
 order by 1, 2, 3

在SQLFiddle上查看这个


如果视图被禁止,只需将每个视图替换为其查询,因此最后一个查询将是

select i.xdate, i.xtime, ifnull(o.xtime, 'missing')
  from (select date(date_time) xdate, time(date_time) xtime from gate_times where gate_op = 'check-in') i
  left join (select date(date_time) xdate, time(date_time) xtime from gate_times where gate_op = 'check-out') o
    on i.xdate = o.xdate

union all

select o.xdate, ifnull(i.xtime, 'missing'), o.xtime
  from (select date(date_time) xdate, time(date_time) xtime from gate_times where gate_op = 'check-in') i
 right join (select date(date_time) xdate, time(date_time) xtime from gate_times where gate_op = 'check-out') o
    on i.xdate = o.xdate
 where i.xdate is null
 order by 1, 2, 3

可以在SQLFiddle查看

【讨论】:

阿米尔,谢谢,但我无法检查这个,因为我不能使用仅查看 SQL 查询。 @Sam 您可以手动将in_gate_timesout_gate_times 替换为视图中的选择。这种方法比我的解决方案更优雅。 阿米尔,哇。您的查询获取信息的速度快了 100 倍。取而代之的是 20 秒,它是 0.2 秒。谢谢。 @Sam 不客气。如果视图被禁止,您可以将最后一个查询中的每个视图用法替换为其查询(在括号中)。如果答案适合你,那就接受吧。【参考方案2】:

可能有更好的方法来执行此操作,但您可以通过以下三部分查询来执行此操作。

首先,找到所有签到并使用子查询选择当天签到后的第一个签出。

select
    date(t1.timedate) as date,
    time(t1.timedate) as checkin,
    (
        select time(t2.timedate)
        from timetracker t2
        where workclock = 'check-out'
          and date(t2.timedate) = date(t1.timedate)
          and t1.timedate < t2.timedate
     ) as checkout
from timetracker t1
where t1.workclock = 'check-in'

+------------+----------+----------+
| date       | checkin  | checkout |
+------------+----------+----------+
| 2018-01-01 | 09:00:00 | NULL     |
| 2018-01-01 | 12:30:40 | NULL     |
| 2018-01-02 | 09:00:00 | 11:29:00 |
+------------+----------+----------+

注意:我假设你错过了 12:30:40 的那一行。

然后在一个单独的查询中,找到所有没有签入的签出。这是通过针对签入行的自联接来完成的。

select
    date(t3.timedate) as date,
    null as checkin,
    time(t3.timedate) as checkout
from timetracker t3
left join timetracker t4
     on date(t3.timedate) = date(t4.timedate) and
        t4.workclock = 'check-in'
where t3.workclock = 'check-out'
  and t4.timedate is null

+------------+---------+----------+
| date       | checkin | checkout |
+------------+---------+----------+
| 2018-01-03 |    NULL | 14:29:00 |
+------------+---------+----------+

union他们在一起。

select
    date(t1.timedate) as date,
    time(t1.timedate) as checkin,
    (
        select time(t2.timedate)
        from timetracker t2
        where workclock = 'check-out'
          and date(t2.timedate) = date(t1.timedate)
          and t1.timedate < t2.timedate
        order by t2.timedate
        limit 1
     ) as checkout
from timetracker t1
where t1.workclock = 'check-in'

union

select
    date(t3.timedate) as date,
    null as checkin,
    time(t3.timedate) as checkout
from timetracker t3
left join timetracker t4
     on date(t3.timedate) = date(t4.timedate) and
        t4.workclock = 'check-in'
where t3.workclock = 'check-out'
    and t4.timedate is null

+------------+----------+----------+
| date       | checkin  | checkout |
+------------+----------+----------+
| 2018-01-01 | 09:00:00 | NULL     |
| 2018-01-01 | 12:30:40 | NULL     |
| 2018-01-02 | 09:00:00 | 11:29:00 |
| 2018-01-03 | NULL     | 14:29:00 |
+------------+----------+----------+

最后一部分是格式化和排序。与其尝试在这个已经很庞大的查询中执行此操作,不如为格式化创建一个新查询。将此查询用作子查询表。

select
    t.date,
    case
    when t.checkin is null then
        'none'
    else
        t.checkin
    end as "checkin",
    case
    when t.checkout is null then
        'none'
    else
        t.checkout
    end as "checkout",
    case
    when checkout is null then
        'missing checkout'
    when checkin is null then
        'missing checkin'
    else
        time(checkout - checkin)
    end as "working-time"
from (
    select ...
    union
    select ...
) t
order by t.date, t.checkin

+------------+----------+----------+------------------+
| date       | checkin  | checkout | working-time     |
+------------+----------+----------+------------------+
| 2018-01-01 | 09:00:00 | none     | missing checkout |
| 2018-01-01 | 12:30:40 | none     | missing checkout |
| 2018-01-02 | 09:00:00 | 11:29:00 | 02:29:00         |
| 2018-01-03 | none     | 14:29:00 | missing checkin  |
+------------+----------+----------+------------------+

或者省点麻烦,在接收端进行格式化。

【讨论】:

施韦恩,谢谢。您的查询效果很好,但我如何添加签入到签出之间的工作时间。有没有办法加快查询速度,因为只获取 22 行(几乎 20 秒)需要很长时间。 添加工作时间结果也有一些错误,如截断不正确的时间值:'55568' ErrorNr。第1292章 @Sam 签入和签出之间的时间已经到了。使用通常的方法来检查性能问题,但我猜想在 where 子句中有 date(timedate) 会降低性能,因为它无法被索引。见this question for work arounds。即使您必须手动展开所有视图,Amir 的回答也更加简洁。 将时间(checkout - checkin) 更改为 TIMEDIFF(checkout,checkin) 修复了截断问题。现在只需要加快这个查询速度

以上是关于计算工作时间的两行之间的时间差的主要内容,如果未能解决你的问题,请参考以下文章

MySQL SELECT 语句的两行之间的区别

如何在 Java 中的两行代码之间创建延迟? [复制]

计算两行之间的时间差

mysql连接具有相同列值的两行并计算某些列值并返回一行中的所有行

在laravel中计算两行的时间差

C# - RegEx - 获取两行之间的字符串