Самый простой способ справиться с этим - рассчитать дни и часы отдельно, а затем просто считать день как 8 часов. Примерно так:
SELECT t.task_name,
SUM(TIMESTAMPDIFF(DAY, DATE(start_date_time), DATE(end_date_time)) * 8 +
TIMESTAMPDIFF(MINUTE, TIME(start_date_time), TIME(end_date_time)) / 60) AS task_time
FROM task t
JOIN task_time tt ON tt.task_id = t.task_id
GROUP BY t.task_name
Вывод (для моих демонстрационных данных)
task_name task_time
task1 16
task2 11
task3 4
Демонстрация на dbfiddle
Обновление
В этом запросе время начала и окончания будет вне 9:00 - 17:00, а рабочие часы будут считаться только с 9:00 до 17:00. Он работает путем принудительного задания времени начала и окончания в диапазоне 9–5 вечера, используя GREATEST
и LEAST
:
SELECT t.task_name,
SUM(TIMESTAMPDIFF(DAY, start_date, end_date) * 8 +
TIMESTAMPDIFF(MINUTE, TIME(start_time), TIME(end_time)) / 60) AS task_time
FROM task t
JOIN (SELECT task_id,
DATE(start_date_time) AS start_date,
DATE(end_date_time) AS end_date,
GREATEST('09:00', LEAST('17:00', TIME(start_date_time))) AS start_time,
GREATEST('09:00', LEAST('17:00', TIME(end_date_time))) AS end_time
FROM task_time) tt ON tt.task_id = t.task_id
GROUP BY t.task_name
Обновленная демоверсия
Обновление 2
Этот запрос возвращает время в виде дней, часов и минут, а не просто часов.
SELECT t.task_name,
days + FLOOR(minutes / 480) AS days,
CASE WHEN minutes < 0 THEN 8 + FLOOR(minutes % 480 / 60)
ELSE FLOOR(minutes % 480 / 60)
END AS hours,
CASE WHEN minutes < 0 THEN (60 + (minutes % 60)) % 60
ELSE minutes % 60
END AS minutes
FROM (SELECT t.task_name,
SUM(TIMESTAMPDIFF(DAY, start_date, end_date)) AS days,
SUM(TIMESTAMPDIFF(MINUTE, TIME(start_time), TIME(end_time))) AS minutes
FROM task t
JOIN (SELECT task_id,
DATE(start_date_time) AS start_date,
DATE(end_date_time) AS end_date,
GREATEST('09:00', LEAST('17:00', TIME(start_date_time))) AS start_time,
GREATEST('09:00', LEAST('17:00', TIME(end_date_time))) AS end_time
FROM task_time) tt ON tt.task_id = t.task_id
GROUP BY t.task_name) t
Обновленная демоверсия