Вот пример/демонстрация одного из возможных подходов:
SELECT r.dt + INTERVAL 7-DAYOFWEEK(r.dt) DAY AS week_ending
, t.userid
, r.dt
, SUM(TIMESTAMPDIFF(SECOND
,GREATEST(r.dt,t.timein)
,LEAST(r.dt+INTERVAL 1 DAY,t.timeout)
)
)/3600 AS hours_worked
FROM (SELECT '2014-09-28' AS rb, '2014-10-11' AS re) dr
JOIN (SELECT DATE(i.timein) AS dt FROM mytable i
UNION
SELECT DATE(o.timeout) FROM mytable o
) r
ON r.dt BETWEEN DATE(dr.rb) AND DATE(dr.re)
JOIN mytable t
ON t.timein < r.dt+INTERVAL 1 DAY
AND t.timeout > r.dt
GROUP BY t.userid, r.dt
ORDER BY week_ending, t.userid, r.dt
ПРИМЕЧАНИЕ: week_ending
возвращает дату в субботу следующую (или) дату работы.
Диапазон дат указан во встроенном виде dr
(диапазон дат), период начала ввода - столбец rb
, дата окончания диапазона - столбец re
. Этот пример показывает двухнедельный диапазон, начиная с воскресенья 2014-09-28 в течение двух полных недель, включая время работы в субботу 2014-10-11. (Значение для re
может быть получено как целое число дней от rb
, чтобы получить 14 полных дней, `rb + INTERVAL 13 DAY)
Сообщается только о часах работы с этими датами или в дни между этими датами , У данного пользователя, у которого не было никакого «времени» работы на заданную дату, не будет возвращена строка для этой даты. (Запрос можно легко настроить, чтобы возвращать строки для всех сотрудников для всех дат в диапазоне и возвращающих нулей.)
Отсутствие таблицы «cal
», содержащей все значения даты, мы можем получить отчетливый список дат значения в диапазоне от самой таблицы; это может быть относительно дорогостоящей операцией для большого количества или строк в таблице. Это не приведет к возврату значений даты, которые не отображаются в столбцах timein или timeout. (Если есть период работы более 24 часов, например, начиная с понедельника и заканчивая в среду. В среду нет других строк, которые имеют тайм-аут или тайм-аут, 24 часа работы в этот день будут опущены ... это, скорее всего, крайний угол случай. Имея четкий список всех возможных значений даты имеющихся в календарной таблице позволяет избежать этой проблемы.)
Followup
вашего комментария звучит, как вам нужно перекрестное произведение между отдельный список дней и отдельный список пользователей, а затем внешнее соединение с таблицей рабочих часов.
JOIN (distinct_list_of_dates_r) r
ON (r_in_specified_week)
CROSS
JOIN (distinct_list_of_userid_u) u
LEFT
JOIN mytable t
ON (t_userid_matches_u)
AND (t_times_matches_r )
А потом GROUP BY
идентификатор пользователя из u
, а не от t
. Вы, скорее всего, захотите заменить значение NULL, возвращаемое с hours_worked
, на 0. Функция MySQL IFNULL
удобна для этого, IFNULL(expr,0)
является сокращением для CASE WHEN expr IS NULL THEN 0 ELSE expr END
.Если вы хотите, чтобы общее количество часов в течение всей недели, а не по отдельным дням, сделайте GROUP BY
в выражении week_ending
, а не на отдельной дате.
Все ли столбцы, относящиеся к проблеме? – Strawberry