Лучший способ запросить MySQL за общее время, отработанное за день - PullRequest
2 голосов
/ 08 октября 2011

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

Типы перфорации: день, прорыв, прорыв и день . Вы могли бы подумать, что я могу просто выбрать *, где date = DATE, но это не сработает, если сотрудник приехал в 23:30 и ушел в 2:00 на следующий день.

Я думал, что подобное может быть лучшим решением, но я не знаю, практично ли это или вообще возможно:

Выберите * из ПОСЛЕДНЕЙ ЗАПИСИ, где employee = ОСТАНОВКА сотрудника при первом появлении «clock in».

Это будет собирать все удары с момента последнего включения сотрудника. Например:

ИМЯ ИМЯ ТИП ВРЕМЯ

45 Джон Дэй ВРЕМЯ

46 Джо Дэй ВРЕМЯ

47 ВРЕМЯ ВЫСТУПЛЕНИЯ Мэри

48 ВРЕМЯ Джо Брейк

49 Джо Брейк ВРЕМЯ

50 ВРЕМЯ ОТДЫХА ДЖОНА

51 Мэри Брейк ВРЕМЯ

52 ДЖО ДА ВРЕМЯ

53 ВРЕМЯ ДНЯ МАРИИ

Так что в примере, о котором я думал, если вы ищете время Джо, он начнет поиск с идентификатора перфорации 53 назад, пока не достигнет перфоратора 46. Результатом будут удары 46, 48, 49 и 51. I Затем можно рассчитать общее время работы. Опять же, я не знаю, возможно ли это вообще.

Я ДЕЙСТВИТЕЛЬНО буду благодарен за любые комментарии / предложения относительно того, как этого добиться, или за любые другие способы, которые могут быть более практичными для этого!

Ответы [ 3 ]

2 голосов
/ 08 октября 2011

Разбивая проблему на этапы, сначала перечислите все записи IN:

SELECT p_in.*
FROM punches p_in
WHERE p_in.type = 'IN'

Далее, напишите подзапрос, чтобы найти следующую запись «OUT» (этот метод предполагает, что id автоматически увеличивается для простоты):

        SELECT MIN(pa.id)
        FROM punches pa
        WHERE pa.type = 'OUT'
        AND pa.name = p_in.name
        AND pa.time > p_in.time

Теперь оберните этот запрос во внешнее выделение, которое выглядит как таблица ударов:

    SELECT pb.id, pb.name, pb.type, pb.time
    FROM punches pb
    WHERE pb.id = (
        SELECT MIN(pa.id)
        FROM punches pa
        WHERE pa.type = 'OUT'
        AND pa.name = p_in.name
        AND pa.time > p_in.time
    )

И используйте это в левом соединении с исходным запросом:

SELECT
    p_in.id     in_id,
    p_in.name   in_name,
    p_in.type   in_type,
    p_in.time   in_time,
    p_out.id    out_id,
    p_out.name  out_name,
    p_out.type  out_type,
    p_out.time  out_time
FROM punches p_in
LEFT JOIN (
    SELECT pb.id, pb.name, pb.type, pb.time
    FROM punches pb
    WHERE pb.id = (
        SELECT MIN(pa.id)
        FROM punches pa
        WHERE pa.type = 'OUT'
        AND pa.name = p_in.name
        AND pa.time > p_in.time
    )
) p_out
WHERE p_in.type = 'IN'

Это даст вам набор результатов, в котором каждая запись «in» имеет следующую соответствующую запись «out». Если нет соответствующей выходной записи, выходные значения будут установлены в нуль.

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

0 голосов
/ 11 октября 2011

Я бы использовал запрос следующим образом:

select workerid, sum(case 
when punchtype = 'in' then -DATEDIFF(MINUTE, '20110901', punchtime)
when punchtype = 'out' then DATEDIFF(MINUTE, '20110901', punchtime)
when punchtype = 'bin' then -DATEDIFF(MINUTE, '20110901', punchtime)
when punchtype = 'bout' then DATEDIFF(MINUTE, '20110901', punchtime)
end )/60 as WorkingHours
from Punchlog
group by workerid

Примечания: Предположим, у меня есть таблица:

create table PunchLog
(workerid int not null,
punchtime datetime not null,
punchtype char(4) not null)

с данными перфорации за несколько дней.

Приведем некоторые данные теста:

insert into PunchLog (workerid, punchtime, punchtype) values (1, '20110901 23:30', 'in')
insert into PunchLog (workerid, punchtime, punchtype) values (1, '20110902 03:30', 'out')
insert into PunchLog (workerid, punchtime, punchtype) values (1, '20110902 00:15', 'bin')
insert into PunchLog (workerid, punchtime, punchtype) values (1, '20110902 01:15', 'bout')
insert into PunchLog (workerid, punchtime, punchtype) values (2, '20110901 09:30', 'in')
insert into PunchLog (workerid, punchtime, punchtype) values (2, '20110901 17:00', 'out')
insert into PunchLog (workerid, punchtime, punchtype) values (2, '20110901 14:30', 'bin')
insert into PunchLog (workerid, punchtime, punchtype) values (2, '20110901 10:00', 'bout')

Время, проведенное на рабочем месте:

T = out - in - (bin -bout) = out - in -bin + bout

, где in, out - время дневного удара и bin, bout - время пробоя.

Если я сгруппируюсь по workerid и приложу sum () к punchtime, тогда у меня будет рабочее время, НО мне нужно, чтобы in и bin время были отрицательными.Также я не могу суммировать дату и время.Таким образом, я изменю punchtime на диапазон от любой даты в прошлом и использую инструкцию case для установки знака:

sum(case 
when punchtype = 'in' then -DATEDIFF(MINUTE, '20110901', punchtime)
when punchtype = 'out' then DATEDIFF(MINUTE, '20110901', punchtime)
. . .
end )/60 as WorkingHours

в моем тесте он дает результат:

workerid  WorkingHours
--------  ------------
1         5
2         3
0 голосов
/ 08 октября 2011

У вас уже есть идентификатор сотрудника, идентификатор типа удара, но вам не хватает идентификатора дня или периода, что значительно упростит задачу:

PunchId  PeriodId  Employee  PunchTypeId  PunchDateTime
-------- --------- --------- ------------ --------------
...
44       1         Mary      Day In       TIME
45       1         John      Day In       TIME
46       1         Joe       Day In       TIME
47       1         Mary      Break Out    TIME
48       1         Joe       Break Out    TIME
49       1         Joe       Break In     TIME
50       1         John      Day Out      TIME
51       1         Mary      Break In     TIME
52       1         Joe       Day Out      TIME
53       1         Mary      Day Out      TIME
54       2         John      Day In       TIME
55       2         Mary      Day In       TIME
56       2         Joe       Day In       TIME
...

Тогда вы можете использовать что-то вроде:

select di.Employee, di.PeriodId, 
    timediff(do.DayOutTime, bi.BreakInTime)
    + timediff(bo.BreakOutTime, di.DayInTime) as PeriodWorkedHours
from
(
    select Employee, PeriodId, PunchDateTime as DayInTime
    from punches
    where PunchTypeId = 'Day In'
) as di
inner join (
    select Employee, PeriodId, PunchDateTime BreakOutTime
    from punches
    where PunchTypeId = 'Break Out'
) as bo on di.Employee = bo.Employee and di.PeriodId = bo.PeriodId
inner join (
    select Employee, PeriodId, PunchDateTime as BreakInTime
    from punches
    where PunchTypeId = 'Break In'
) as bi on di.Employee = bi.Employee and di.PeriodId = bi.PeriodId
inner join (
    select Employee, PeriodId, PunchDateTime as DayOutTime
    from punches
    where PunchTypeId = 'Day Out'
) as do on di.Employee = do.Employee and di.PeriodId = do.PeriodId

Предполагается, что в PeriodId у всех сотрудников по четыре удара (Day In, Break out, Break In и Day Out).Кроме того, PeriodId не должен быть привязан к дате для обработки случая, который вы упомянули в своем вопросе, когда время штамповки пересекает границы дня.Скорее, увеличьте PeriodId для сотрудника после вставки последнего удара за период (т. Е. Выходного дня).

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