MySQL每天的时间间隔无间隙

时间:2019-03-20 16:37:46

标签: mysql sql

我有一个类似以下示例的表格: enter image description here

我需要做的是返回每天的覆盖范围(操作员在现场的小时数)。面临的挑战是,我需要忽略覆盖范围上的差距,而不能重复计算两个操作员同时登录的小时数。例如,下图是表格的直观表示。 enter image description here

图像的逻辑如下:

  • 操作员A:10点登录,中午退出,总计2个小时
  • 操作员B:1点登录,3点退出,总计2个小时
  • 操作员A:回来并在2点登录,然后在5点退出,总共进行了3个小时,但与操作员A重叠了1个小时,所以我无法算作1个小时,否则我将重复计算覆盖率

因此,没有重叠的总覆盖时间为6小时,我需要查询产生的值。到目前为止,我可以忽略重复计数,方法是将每天的最小日期中的最大值减去两个值:

SELECT YEAR, WEEK, SUM(HOURS)
FROM
(SELECT 
  YEAR(SignedIn) AS YEAR, 
  WEEK(SignedIn) AS WEEK, 
  DAY(SignedIn) AS DAY,
  time_to_sec(timediff(MAX(SignedOut), MIN(SignedIn)))/ 3600 AS HOURS
 FROM OperatorLogs
 GROUP BY YEAR, WEEK, DAY) As VirtualTable
GROUP BY YEAR, WEEK

之所以产生7,是因为它需要首次登录(上午10点)并计算直到最后一次退出(下午4:00)为止的小时数。但是,它包括覆盖范围(12-1)中的缺口,不应包括在内。我不确定如何从总时数中删除该时间,同时在出现重叠时也不会重复计算,即从2-3开始,即使两个不同的操作员都在一个小时内,也应该只有1小时的覆盖时间。任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:1)

对不起,工作打扰了我。

这是我的工作解决方案,由于联接的(相对)昂贵的性质,我不认为它是最佳的,但是我基于“转移”从未跨越多天的软规则对它进行了稍微的优化。 / p>

SELECT
  calendar_date,
  SUM(coverage_seconds) / 3600   AS coverage_hours
FROM
(  
  -- Signins that didn't happen within another operators shift
  SELECT DISTINCT
    DATE(e.signedin)                          AS calendar_date,
    -(UNIX_TIMESTAMP(e.signedin) MOD 86400)   AS coverage_seconds
  FROM
    OperatorLogs   e
  LEFT JOIN
    OperatorLogs   o
      ON  o.signedin  >= DATE(e.signedin)
      AND o.signedin  <       e.signedin
      AND o.signedout >=      e.signedin
  WHERE
    o.signedin IS NULL

  UNION ALL

  -- Signouts that didn't happen within another operators shift
  SELECT DISTINCT
    DATE(e.signedout)                          AS calendar_date,
    +(UNIX_TIMESTAMP(e.signedout) MOD 86400)   AS coverage_seconds
  FROM
    OperatorLogs   e
  LEFT JOIN
    OperatorLogs   o
      ON  o.signedin  >= DATE(e.signedout)
      AND o.signedin  <=      e.signedout
      AND o.signedout >       e.signedout
  WHERE
    o.signedin IS NULL
)
  AS coverage_markers
GROUP BY
  calendar_date
;

随时使用更严格的数据进行测试...

(请注意,为使示例数据与excel图像匹配,您的第一个班次应该在上午9点开始)