Это не похоже на прямую проблему пропусков и островков.
Во-первых, существует проблема выявления достаточно больших промежутков между посещениями.
И это означает, что необходимо учитывать как время начала, так и время окончания.
Затем, когда эти промежутки найдены, их необходимо развернуть с 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 здесь