Как мне рассчитать промежутки времени на основе списка приходов и выездов?
-
07-07-2019 - |
Вопрос
Я пишу простую программу учета времени для управления своими собственными проектами.Я большой поклонник хранения кода отчетов в базе данных, поэтому я попытался создать несколько процедур, которые генерируют счета, табели учета рабочего времени и т. д.
У меня есть таблица, содержащая действия часов, IE «Punch In» и «Punch Out».Он также содержит пользователя, выполнившего это действие, проект, связанный с этим действием, и текущую дату/время.
Я могу выбрать из этой таблицы, чтобы получить входные часы для определенного времени/проекта/и пользователя, но я хочу агрегировать их так, чтобы каждый входной и выходной сигнал преобразулся из двух строк в одну строку, содержащую общее время.
Например, вот пример вывода:
ClockActionID ActionType DateTime
-------------------- ---------- -----------------------
17 1 2008-11-08 18:33:56.000
18 2 2008-11-08 18:33:59.587
19 1 2008-11-08 18:34:01.023
20 2 2008-11-08 18:34:02.037
21 1 2008-11-08 18:45:06.317
22 2 2008-11-08 18:46:14.597
23 1 2008-11-08 18:46:16.283
24 2 2008-11-08 18:46:17.173
25 1 2008-11-08 18:50:37.830
26 2 2008-11-08 18:50:39.737
27 1 2008-11-08 18:50:40.547
(11 row(s) affected)
Где ActionType 1 — «ClockIn», а ActionType 2 — «ClockOut».Для краткости я также удалил столбцы «Пользователь», «Проект» и «Описание».
Мне нужно сгенерировать на чистом SQL набор результатов вроде:
Description | Total Time
Для каждой пары ClockIn/ClockOut.
Я полагаю, что на самом деле это будет довольно просто, я просто не совсем уверен, как к этому подойти.
РЕДАКТИРОВАТЬ:Пользователь сможет синхронизироваться с несколькими проектами одновременно, хотя, если сначала сузить набор результатов до одного проекта, это не должно иметь никакого значения для логики здесь.
Решение
Я согласен, что дизайн не самый лучший - основанная на событиях структура с одной строкой для начала и конца, вероятно, сэкономит вам много времени. В этом случае вы можете создать запись с нулевой конечной датой, когда кто-то запускает таймер. Затем заполните дату окончания, когда он истечет.
Но это не то, что вы спросили. Это решение вашей проблемы:
DECLARE @clock TABLE (ClockActionID INT PRIMARY KEY IDENTITY, ActionType INT, ActionDateTime DATETIME)
INSERT INTO @clock (ActionType, ActionDateTime) VALUES (1,'20080101 00:00:00')
INSERT INTO @clock (ActionType, ActionDateTime) VALUES (2,'20080101 00:01:00')
INSERT INTO @clock (ActionType, ActionDateTime) VALUES (1,'20080101 00:02:00')
INSERT INTO @clock (ActionType, ActionDateTime) VALUES (2,'20080101 00:03:00')
INSERT INTO @clock (ActionType, ActionDateTime) VALUES (1,'20080101 00:04:00')
INSERT INTO @clock (ActionType, ActionDateTime) VALUES (2,'20080101 00:05:00')
INSERT INTO @clock (ActionType, ActionDateTime) VALUES (1,'20080101 00:06:00')
INSERT INTO @clock (ActionType, ActionDateTime) VALUES (2,'20080101 00:07:00')
INSERT INTO @clock (ActionType, ActionDateTime) VALUES (1,'20080101 00:08:12')
INSERT INTO @clock (ActionType, ActionDateTime) VALUES (2,'20080101 00:09:00')
-- Get the range
SELECT ActionDateTime CheckIn,
(SELECT TOP 1 ActionDateTime
FROM @clock C2
WHERE C2.ActionDateTime > C.ActionDateTime) CheckOut
FROM @clock C
WHERE ActionType = 1
-- Get the duration
SELECT DATEDIFF(second, ActionDateTime,
(SELECT TOP 1 ActionDateTime
FROM @clock C2
WHERE C2.ActionDateTime > C.ActionDateTime)
) / 60.0 Duration_Minutes
FROM @clock C
WHERE ActionType = 1
Обратите внимание, что я использую табличную переменную, которая работает с MS SQL Server только для тестирования. Изменить по мере необходимости. Также обратите внимание, что SQL Server 2000 плохо работает с такими запросами. Вот результаты теста:
CheckIn CheckOut
2008-01-01 00:00:00.000 2008-01-01 00:01:00.000
2008-01-01 00:02:00.000 2008-01-01 00:03:00.000
2008-01-01 00:04:00.000 2008-01-01 00:05:00.000
2008-01-01 00:06:00.000 2008-01-01 00:07:00.000
2008-01-01 00:08:12.000 2008-01-01 00:09:00.000
Duration_Minutes
1.000000
1.000000
1.000000
1.000000
0.800000
Другие советы
Я думаю, что ваша оригинальная схема хранения имеет недостатки. Вы смотрите на это с точки зрения часов, отсюда ClockAction. Почему не с точки зрения проекта?
TABLE ProjectAction (проект, идентификатор пользователя, тип, начало, конец)
Тогда простая GROUP BY должна сработать.
Задумывались ли вы о таких вещах, как:
- кто-то забывает уйти, и, таким образом, получается, что он пришел дважды?
- кто-то забывает прийти на работу, и, таким образом, получается, что он ушел дважды?
В первом случае правильно ли просто игнорировать второй приход?Принятый ответ привяжет оба входа к одному и тому же выходу.Это верно?
Во втором случае второй аут будет проигнорирован.Это верно?