Как мне рассчитать промежутки времени на основе списка приходов и выездов?

StackOverflow https://stackoverflow.com/questions/275562

Вопрос

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

У меня есть таблица, содержащая действия часов, 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 должна сработать.

Задумывались ли вы о таких вещах, как:

  • кто-то забывает уйти, и, таким образом, получается, что он пришел дважды?
  • кто-то забывает прийти на работу, и, таким образом, получается, что он ушел дважды?

В первом случае правильно ли просто игнорировать второй приход?Принятый ответ привяжет оба входа к одному и тому же выходу.Это верно?

Во втором случае второй аут будет проигнорирован.Это верно?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top