Запрос, чтобы найти промежуток времени между посещениями - PullRequest
0 голосов
/ 30 декабря 2018

Я создаю небольшое приложение для регистрации посещений, и я застрял в поиске окон свободного времени между посещениями.

Есть две таблицы, это упрощенная структура:

working_hours- начальное время- время окончания

визит- начальное время- время окончания- visit_status

visit_status со значением "2" являются отмененными посещениями, поэтому мы не включаем их

Теперь небольшой пример:

У сотрудника есть свои рабочие часы с перерывамивключены, например:

| start_time          | end_time            |
| 2018-12-29 08:00:00 | 2018-12-29 12:00:00 |
| 2018-12-29 12:30:00 | 2018-12-29 16:00:00 |

В приложении уже зарегистрированы посещения, они могут иметь различную продолжительность.Время посещения включает перерыв, поэтому следующий визит может начаться сразу после.Допустим, у нас есть такие посещения:

| start_time          | end_time            | visit_status |
| 2018-12-29 08:00:00 | 2018-12-29 08:30:00 | 1            |
| 2018-12-29 09:00:00 | 2018-12-29 10:00:00 | 1            |
| 2018-12-29 10:00:00 | 2018-12-29 10:40:00 | 1            |
| 2018-12-29 10:40:00 | 2018-12-29 11:10:00 | 2            |
| 2018-12-29 11:10:00 | 2018-12-29 11:40:00 | 0            |
| 2018-12-29 12:30:00 | 2018-12-29 13:00:00 | 0            |
| 2018-12-29 13:00:00 | 2018-12-29 14:00:00 | 0            |
| 2018-12-29 15:30:00 | 2018-12-29 16:00:00 | 0            |

Моя цель - создать запрос, который покажет мне доступное время начала для тридцатиминутного посещения, включая рабочее время. В этом конкретном случае результат должен быть следующим:

  • 8: 30
  • 10: 40
  • 14: 00
  • 14: 30
  • 15: 00

1 Ответ

0 голосов
/ 30 декабря 2018

Это не похоже на прямую проблему пропусков и островков.

Во-первых, существует проблема выявления достаточно больших промежутков между посещениями.
И это означает, что необходимо учитывать как время начала, так и время окончания.

Затем, когда эти промежутки найдены, их необходимо развернуть с 30-минутными интервалами.

Чтобы развернуть пробелы, вы можете связать таблицу чисел.
Лучше создать постоянную.
В приведенном ниже примере просто добавляются значения, чтобы не усложнять этот ответ.
Но есть и другие методы.Например, здесь

create table nums (num int primary key not null);
insert into nums (num) VALUES 
(00),(01),(02),(03),(04),(05),(06),(07),(08),(09),
(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),
(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),
(30),(31),(32),(33),(34),(35),(36),(37),(38),(39),
(40),(41),(42),(43),(44),(45),(46),(47),(48),(49);

Тогда такой запрос будет работать в MySql 5.7

SELECT DISTINCT 
 CAST(gaps.start_dt + INTERVAL (nums.num * 30) MINUTE AS TIME) as start_time
FROM
(
  SELECT rnk, MIN(prev_dt) as start_dt, MIN(start_dt) as end_dt
  FROM
  (
    SELECT 
    start_time AS start_dt, 
    end_time as end_dt, 
    @prev_dt as prev_dt,
    -- DATE(@prev_dt) + INTERVAL (CEIL(TIME_TO_SEC(@prev_dt) / 600) * 600) SECOND as prev_dt,
    (CASE
     WHEN @prev_dt = start_time AND @prev_dt := end_time THEN @rnk
     WHEN @prev_dt := end_time THEN @rnk := @rnk + 1
     END) AS rnk
    FROM visits
    CROSS JOIN (SELECT @prev_dt := null, @rnk := 0) vars
    WHERE visit_status <> 2
    ORDER BY start_time
  ) AS vst
  GROUP BY rnk
  HAVING CAST(MIN(start_dt) AS DATE) = CAST(MIN(prev_dt) AS DATE)
     AND TIMEDIFF(MIN(start_dt), MIN(prev_dt)) >= CAST('00:30' AS TIME)
) gaps
JOIN working_hours wrk 
  ON wrk.start_time <= gaps.start_dt AND wrk.end_time >= gaps.end_dt
JOIN nums
  ON nums.num BETWEEN 0 AND 47
 AND gaps.start_dt + INTERVAL (nums.num * 30) MINUTE < gaps.end_dt;

Подзапрос «vst» присваивает ранжирование датам и времени поиспользуя переменные.

Затем подзапрос «гэпы» группирует их по этому ранжированию, чтобы найти начало и конец гэпов.

Затем присоединяются working_hours.
И таблица «nums» используется для развертывания времени с 30-минутными интервалами.

Результат:

start_time
08:30:00
10:40:00
14:00:00
14:30:00
15:00:00

Вы можете проверить его на db <> fiddle здесь

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