Ежедневное покрытие MySQL без пробелов - PullRequest
0 голосов
/ 20 марта 2019

У меня есть таблица, как в следующем примере: enter image description here

Что мне нужно сделать, это вернуть покрытие (количество часов, в течение которых оператор / операторы были на месте) для каждого дня,Проблема заключается в том, что мне нужно игнорировать пробелы в покрытии, а не часы с двойным счетом, когда два оператора были зарегистрированы одновременно.Например, изображение ниже представляет собой визуальное представление таблицы.enter image description here

Логика изображения следующая:

  • Оператор A: вход в 10 и выход из полудня в общей сложности 2часы
  • Оператор B: вход в 1 и выход из 3 в общей сложности 2 часа
  • Оператор A: возврат и вход в 2 и выход из 5 в общей сложности3 часа, но 1 час перекрывается с оператором A, поэтому я не могу посчитать этот 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 AM) и вычисляет часыдо последнего выхода (16:00).Тем не менее, он включает в себя разрыв в охвате (12 - 1), который не должен быть включен.Я не уверен, как убрать это время из общего количества часов, а также не учитывать дважды, когда есть наложение, то есть из 2-3 должно быть только 1 час покрытия, даже если на месте работают два отдельных оператора, каждый из которых вводит один час.Любая помощь приветствуется.

1 Ответ

1 голос
/ 21 марта 2019

Извините, работа прервала меня.

Вот мое рабочее решение, я не уверен, что оно оптимально из-за (относительно) дорогой природы объединений, но я немного оптимизировал его, основываясь на мягком правиле, согласно которому «сдвиги» никогда не охватывают несколько дней.

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:00)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...