Раздельные записи времени в полночь - PullRequest
6 голосов
/ 19 января 2012

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

Итак, если у меня есть:

EmployeeId   InTime                     OutTime
---          -----------------------    -----------------------
1            2012-01-18 19:50:04.437    2012-01-19 03:30:02.433

Какой, по вашему мнению, самый элегантный способ разделить эту запись следующим образом:

EmployeeId   InTime                     OutTime
---          -----------------------    -----------------------
1            2012-01-18 19:50:04.437    2012-01-19 00:00:00.000
1            2012-01-19 00:00:00.000    2012-01-19 03:30:02.433

И да, я тщательно продумал, какое влияние это может оказать на существующую функциональность ... именно поэтому я решаю сделать это во временной таблице, которая не повлияет на существующую функциональность.

Ответы [ 4 ]

6 голосов
/ 19 января 2012

Это может помочь:

DECLARE @tbl TABLE 
    (
        EmployeeId INT,
        InTime DATETIME,
        OutTime DATETIME
    )

INSERT INTO @tbl(EmployeeId,InTime,OutTime) VALUES (1,'2012-01-18 19:50:04.437','2012-01-19 03:30:02.433')
INSERT INTO @tbl(EmployeeId,InTime,OutTime) VALUES (2,'2012-01-18 19:50:04.437','2012-01-18 20:30:02.433')
INSERT INTO @tbl(EmployeeID,InTime,OutTime) VALUES (3,'2012-01-18 16:15:00.000','2012-01-19 00:00:00.000')
INSERT INTO @tbl(EmployeeID,InTime,OutTime) VALUES (4,'2012-01-18 00:00:00.000','2012-01-18 08:15:00.000')
SELECT
    tbl.EmployeeId,
    tbl.InTime,
    DATEADD(dd, DATEDIFF(dd, 0, tbl.OutTime), 0) AS OutTime
FROM
    @tbl AS tbl
WHERE
    DATEDIFF(dd,tbl.InTime,tbl.OutTime)=1
UNION ALL
SELECT
    tbl.EmployeeId,
    CASE WHEN DATEDIFF(dd,tbl.InTime,tbl.OutTime)=1
        THEN DATEADD(dd, DATEDIFF(dd, 0, tbl.OutTime), 0)
        ELSE tbl.InTime
    END AS InTime,
    tbl.OutTime
FROM @tbl AS tbl
ORDER BY EmployeeId
2 голосов
/ 20 января 2012

Следующее решение использует таблицу чисел (в форме подмножества системной таблицы master..spt_values) для разделения временных диапазонов. Он может разделять диапазоны, охватывающие произвольное количество дней (до 2048 с spt_values, но с вашей собственной таблицей чисел вы можете установить другой максимум). Конкретные случаи 1- и 2-дневных диапазонов охвата здесь не рассматриваются, но я считаю, что метод достаточно легок для того, чтобы вы могли попробовать:

;
WITH LaborHours (EmployeeId, InTime, OutTime) AS (
  SELECT
    1,
    CAST('2012-01-18 19:50:04.437' AS datetime),
    CAST('2012-01-18 03:30:02.433' AS datetime)
),
HoursSplit AS (
  SELECT
    h.*,
    SubInTime  = DATEADD(DAY, DATEDIFF(DAY, 0, h.InTime) + v.number + 0, 0),
    SubOutTime = DATEADD(DAY, DATEDIFF(DAY, 0, h.InTime) + v.number + 1, 0)
  FROM LaborHours h
    INNER JOIN master..spt_values v
      ON number BETWEEN 0 AND DATEDIFF(DAY, h.InTime, h.OutTime)
  WHERE v.type = 'P'
),
HoursSubstituted AS (
  SELECT
    EmployeeId,
    InTime  = CASE WHEN InTime  > SubInTime  THEN InTime  ELSE SubInTime  END,
    OutTime = CASE WHEN OutTime < SubOutTime THEN OutTime ELSE SubOutTime END
  FROM HoursSplit
)
SELECT *
FROM HoursSubstituted

По сути, это двухэтапный метод.

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

Далее мы сравниваем начало поддиапазона с началом диапазона, чтобы увидеть, является ли он первым поддиапазоном, и в этом случае мы используем InTime в качестве его начала. Точно так же мы сравниваем окончания, чтобы увидеть, должны ли мы использовать OutTime или только полночь в качестве конца этого поддиапазона.

1 голос
/ 19 января 2012

Если для отчета, то вы должны просто иметь возможность выполнить запрос / объединение, которые дают две записи во время этих условий, начиная с исходной, начиная с ... Без SQL-Server 2008 я могу предложить только псевдокодзапрос для вас.

Первая часть получает все записи в зависимости от того, какое условие диапазона вы хотите показать.Значение «OutTime» является условным ... если оно в один и тот же день, то переход отсутствует, просто используйте время ожидания.Если это на следующий день, используйте приведение для динамического построения даты «ГГГГ-ММ-ДД» (по умолчанию время 00:00:00) в соответствии с желаемым временем OUT.

UNION будет забирать ТОЛЬКО те же самые записи, которые были квалифицированы в ПЕРВЫХ, где даты ввода / вывода различны.Таким образом, мы ЗНАЕМ, что мы хотим, чтобы OutTime действовал как InTime, но на основе времени «00:00:00», поэтому выполняется точно такое же преобразование поля даты / времени, и для этих записей простоиспользуйте окончательное значение «OutTime» как есть.

Дополнительный столбец для «TimeSplit» со значением «1» или «2» предназначен для того, чтобы убедиться, что мы все еще можем группировать по идентификатору сотрудника, но с этогочто записи «1» (начальная смена) являются первыми, а затем любые записи для соответствующего человека имеют запись «2» для перекрытия дня в своей смене.

select
      tc.EmployeeID,
      '1' as TimeSplit,
      tc.InTime,
      case when datepart( dd, tc.InTime ) = datepart( dd, tc.OutTime )
         then tc.OutTime 
         else CAST( CAST( datepart(yyyy, tc.OutTime ) AS varchar) 
              +'-'+ CAST( datepart( mm, tc.OutTime ) AS varchar) 
              +'-'+ CAST( datepart( dd, tc.OutTime ) AS varchar) AS DATETIME)
         end as OutTime
   from 
      TimeCard tc
   where
      YourDateRangeConditions...
   ORDER BY
      tc.EmployeeID, 
      TimeSplit
UNION ALL
select
      tc.EmployeeID,
      '2' as TimeSplit,
      CAST(    CAST( datepart(yyyy, tc.OutTime ) AS varchar) 
         +'-'+ CAST( datepart( mm, tc.OutTime ) AS varchar) 
         +'-'+ CAST( datepart( dd, tc.OutTime ) AS varchar) AS DATETIME)
         end as InTime
      tc.OutTime
   from 
      TimeCard tc
   where
      YourDateRangeConditions...
      AND NOT datepart( dd, tc.InTime ) = datepart( dd, tc.OutTime )
0 голосов
/ 19 января 2012

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

Для добавления новой строки:

insert into table ("EMPLOYEE_ID","INTIME","OUTTIME") values
SELECT EMPLOYEE_ID,date(INTIME),OUTTIME
FROM table
where date(intime) < date(outtime)

Обновление оригинальной строки:

update table 
set outtime =date(outtime) 
where date(intime)= date(outtime)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...