Мэри и другие ночные смены
Сначала поговорим о Мэри и других ночных сменщиках.Мы можем найти их в штампах, когда отфильтровываем все в штампах, где:
- В тот же день не существует ни одного выходного штампа, то есть после штампа.
- Но выходной штампна следующий день существует, и нет штампа на следующий день, который существует до этого штампа.
Для штампа мы используем 00:00 часов следующего дня штампа..
SELECT d.`timestamp` `timestamp_in`,
timestampadd(second, -1 * second(d.`timestamp`), timestampadd(minute, -1 * minute(d.`timestamp`), timestampadd(hour, -1 * hour(d.`timestamp`), timestampadd(day, 1, (d.`timestamp`))))) `timestamp_out`,
d.`name`
FROM `door` d
WHERE d.`inout` = 1
AND NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND di.`timestamp` > d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
AND EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND dii.`timestamp` < di.`timestamp`
AND dii.`inout` = 1))
Чтобы получить штампы ночных смен, мы можем использовать ту же логику, но все переключаем:
>
становится <
в сравнениях дати наоборот. - Если для штампа есть
0
, укажите 1
для штампа и наоборот. - Вместо следующего дня сравните спредыдущий день.
Для штампа «in» мы используем 00:00 часов дня штампа «out».
Если мы UNION ALL
оба запроса, мы получаем часть дляночные смены.
остальная часть банды, также известные как смены дня
Получение штампов на деньсейчас легко.Нам просто нужно отменить условие, которое мы использовали, чтобы найти ночных сменщиков.Чтобы получить входящие штампы:
SELECT d.`timestamp`,
d.`name`
FROM `door` d
WHERE d.`inout` = 1
AND (EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND di.`timestamp` > d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
OR NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND dii.`timestamp` < di.`timestamp`
AND dii.`inout` = 1)))
Для выходных штампов мы снова можем поменять местами, >
становится <
и т. Д., Как указано выше.
Конечно, здесь мыне может генерировать входящие штампы для выходных штампов и наоборот.Мы должны были бы FULL OUTER JOIN
печатать на штампах и на штампах дневных оборотней.Но MySQL по крайней мере в более низких версиях не поддерживает эту операцию.Таким образом, мы UNION
делаем два LEFT JOIN
, меняя роли во втором LEFT JOIN
по сравнению с первым.
Собираем все вместе
Теперьмы можем просто UNION ALL
ночные и дневные смены, чтобы получить полный результат за все дни.И мы можем SELECT FROM
, чтобы получить результат за определенный день:
SELECT coalesce(x.`timestamp_in`, '-') `d1-in-timestamp`,
coalesce(x.`timestamp_out`, '-') `d0-out-timestamp`,
x.`name`
FROM (SELECT r.`timestamp` `timestamp_in`,
s.`timestamp` `timestamp_out`,
r.`name`
FROM (SELECT d.`timestamp`,
d.`name`
FROM `door` d
WHERE d.`inout` = 1
AND (EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND di.`timestamp` > d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
OR NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND dii.`timestamp` < di.`timestamp`
AND dii.`inout` = 1)))) r
LEFT JOIN (SELECT d.`timestamp`,
d.`name`
FROM `door` d
WHERE d.`inout` = 0
AND (EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND di.`timestamp` < d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
OR NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND dii.`timestamp` > di.`timestamp`
AND dii.`inout` = 0)))) s
ON s.`name` = r.`name`
AND date(s.`timestamp`) = date(r.`timestamp`)
AND s.`timestamp` > r.`timestamp`
UNION
SELECT u.`timestamp` `timestamp_in`,
t.`timestamp` `timestamp_out`,
t.`name`
FROM (SELECT d.`timestamp`,
d.`name`
FROM `door` d
WHERE d.`inout` = 0
AND (EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND di.`timestamp` < d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
OR NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND dii.`timestamp` > di.`timestamp`
AND dii.`inout` = 0)))) t
LEFT JOIN (SELECT d.`timestamp`,
d.`name`
FROM `door` d
WHERE d.`inout` = 1
AND (EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND di.`timestamp` > d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
OR NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND dii.`timestamp` < di.`timestamp`
AND dii.`inout` = 1)))) u
ON u.`name` = t.`name`
AND date(u.`timestamp`) = date(t.`timestamp`)
AND u.`timestamp` < t.`timestamp`
UNION
SELECT d.`timestamp` `timestamp_in`,
timestampadd(second, -1 * second(d.`timestamp`), timestampadd(minute, -1 * minute(d.`timestamp`), timestampadd(hour, -1 * hour(d.`timestamp`), timestampadd(day, 1, (d.`timestamp`))))) `timestamp_out`,
d.`name`
FROM `door` d
WHERE d.`inout` = 1
AND NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND di.`timestamp` > d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
AND EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 0
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL 1 DAY)
AND dii.`timestamp` < di.`timestamp`
AND dii.`inout` = 1))
UNION ALL
SELECT timestampadd(second, -1 * second(d.`timestamp`), timestampadd(minute, -1 * minute(d.`timestamp`), timestampadd(hour, -1 * hour(d.`timestamp`), d.`timestamp`))) `timestamp_in`,
d.`timestamp` `timestamp_out`,
d.`name`
FROM `door` d
WHERE d.`inout` = 0
AND NOT EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND di.`timestamp` < d.`timestamp`
AND date(di.`timestamp`) = date(d.`timestamp`))
AND EXISTS (SELECT *
FROM `door` di
WHERE di.`name` = d.`name`
AND di.`inout` = 1
AND date(di.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND NOT EXISTS (SELECT *
FROM `door` dii
WHERE dii.`name` = di.`name`
AND date(dii.`timestamp`) = date_add(date(d.`timestamp`), INTERVAL -1 DAY)
AND dii.`timestamp` > di.`timestamp`
AND dii.`inout` = 0))) x
WHERE date(x.`timestamp_in`) = '2018-04-02'
OR date(x.`timestamp_out`) = '2018-04-02'
ORDER BY x.`name`,
x.`timestamp_in`;
db <> fiddle