SQL - получение суммы раз между несколькими диапазонами - PullRequest
0 голосов
/ 20 февраля 2019

enter image description here

На этом рисунке показано то, что я пытался выполнить в MSSQL.

Это расписание с несколькими временными диапазонами, от A до Bэто сверхурочное время, обычная работа B - C утром, обеденное время C – D, обычная работа D – E днем ​​и E – F, сверхурочное время.

Ради простоты я объявилвручную A, B, C, D, E и F до фиксированных значений.В действительности они будут подаваться из другой таблицы, но это не относится к данной проблеме.

Вертикальные стрелки представляют время включения / выключения тактирования.

Итак, скажем, в 8 утра иOUT это 7 вечера. * 10101

Как я могу получить следующее?

  • Работа: 8 ч

  • Обед: 1 ч

  • Сверхурочные: 2 ч

Без использования путаницы IF и зная, что IN может быть в середине B иНапример, C.

У кого-то, работающего в 11 утра и заканчивающего в 7 вечера, теперь будет работа = 6 часов, обед = 1 час, сверхурочные = 1 час.

Спасибо.

Как пример, мой текущий код спагетти, конечно, есть более элегантный (и более чистый) способ достигнуть этого.

declare @clockIn time = '08:00'
declare @clockOut time = '19:00'

declare @a time= '00:00'
...all possible limits defined here...
declare @f time= '23:59'

tests using C to D only:

-- in block
IF(@clockIn >= @c
   AND @clockOut <= @d)
    BEGIN
        SET @at = @at + ABS(DATEDIFF(hour, @clockIn, @clockOut));
    END;
-- outside block
IF(@clockIn < @c
   AND @clockOut > @d)
    BEGIN
        SET @at = @at + ABS(DATEDIFF(hour, @c, @d));
    END;
-- cIn in block, cOut outside
IF(@clockIn >= @c
   AND @clockOut > @d)
    BEGIN
        SET @at = @at + ABS(DATEDIFF(hour, @clockIn, @d));
    END;
-- cIn outside, cIn inside
IF(@clockIn < @c
   AND @clockOut <= @d)
    BEGIN
        SET @at = @at + ABS(DATEDIFF(hour, @c, @clockOut));
    END
select @ax as overtime,@at as work,@ai as lunch

1 Ответ

0 голосов
/ 20 февраля 2019

Я не могу придумать, как вы можете сделать это, не встроив логику «если-то», так что все зависит от того, как вы это делаете.Я думаю, что самый простой (и, возможно, самый читаемый) способ сделать это - просто использовать выражение case для каждого требуемого времени, возможно, оставив некоторые комментарии о логике, чтобы каждый, кто сталкивается с ним, мог легче понять, как он работает.

Например, что-то вроде этого должно быть достаточно простым, без излишней путаницы (IMO):

DECLARE @Clock TABLE (Clockin TIME NOT NULL, Clockout TIME NOT NULL);
INSERT @Clock VALUES ('12:30', '19:00');

DECLARE @Times TABLE (WorkStart TIME NOT NULL, LunchStart TIME NOT NULL, LunchEnd TIME NOT NULL, WorkEnd TIME NOT NULL);
INSERT @Times VALUES ('09:00', '12:00', '13:00', '18:00');

SELECT 
    OverTime = 
        (CASE -- Overtime hours in the morning.
            WHEN C.Clockin < T.WorkStart -- Clocked in before official work start.
            THEN DATEDIFF(MINUTE, C.Clockin, CASE WHEN C.Clockout > T.WorkStart THEN T.WorkStart ELSE C.Clockout END) 
            ELSE 0 
        END + 
        CASE -- Overtime hours in the evening.
            WHEN C.Clockout > T.WorkEnd -- Clocked out after official work end.
            THEN DATEDIFF(MINUTE, CASE WHEN C.Clockin > T.WorkEnd THEN C.Clockin ELSE T.WorkEnd END, C.Clockout) 
            ELSE 0 
        END) / 60.0,
    Work =
        (CASE -- Work hours for the morning.
            WHEN C.Clockout > T.WorkStart AND C.Clockin < T.LunchStart -- Clocked out after official work start and clocked in before official lunch start.
            THEN DATEDIFF(MINUTE, CASE WHEN C.Clockin < T.WorkStart THEN T.WorkStart ELSE C.Clockin END, CASE WHEN C.Clockout < T.LunchStart THEN C.Clockout ELSE T.LunchStart END) 
            ELSE 0
        END + 
        CASE -- Work hours for the afternoon.
            WHEN C.Clockin < T.WorkEnd AND C.Clockout > T.LunchEnd -- Clocked out after official lunch end and clocked in before official work end.
            THEN DATEDIFF(MINUTE,CASE WHEN C.Clockin < T.LunchEnd THEN T.LunchEnd ELSE C.Clockin END, CASE WHEN C.Clockout < T.WorkEnd THEN C.Clockout ELSE T.WorkEnd END) 
            ELSE 0
        END) / 60.0,
    Lunch =
        CASE -- Lunch hours.
            WHEN C.Clockin < T.LunchEnd AND C.Clockout > T.LunchStart -- Clocked in before official lunch end and clocked out after official lunch start.
            THEN DATEDIFF(MINUTE, CASE WHEN C.Clockin > T.LunchStart THEN C.Clockin ELSE T.LunchStart END, CASE WHEN C.Clockout < T.LunchEnd THEN C.Clockout ELSE T.LunchEnd END) 
            ELSE 0
        END / 60.0
FROM @Clock AS C
CROSS JOIN @Times AS T;
...