meetsur
meetsur

Reputation: 75

Calculate Work Time from Shift and Break Table

I am using SQL Server 2008. I have two table Shift and Break with following data: The shifts will be 7 days week.

Data in SHIFT TABLE

ID  Desc Start_Time End_Time
1   1st 07:20:00    15:20:00
2   2nd 15:20:00    23:20:00
3   3rd 23:20:00    07:20:00

Data in BREAK TABLE

ID  Desc                    Start_Time  End_Time
1   1st Shift - 1st break   09:10:00    09:25:00
2   1st Shift - Lunch       11:30:00    12:05:00
3   1st Shift - 2nd break   13:30:00    13:45:00
4   2nd Shift - 1st break   17:10:00    17:25:00
5   2nd Shift - Lunch       19:30:00    20:05:00
6   2nd Shift - 2nd break   21:30:00    21:45:00
7   3rd Shift - 1st break   01:10:00    01:25:00
8   3rd Shift - Lunch       03:30:00    04:05:00
9   3rd Shift - 2nd break   05:30:00    05:45:00

The output needs to be something like:

Start_Time   End Time
07:20:00     09:10:00
09:25:00     11:30:00
12:05:00     13:30:00
13:30:00     15:20:00
....

I am new with joins, Can anyone help me on join. Also if you think any changes in table structure will be helpful please suggest.

Upvotes: 4

Views: 2411

Answers (1)

Matt
Matt

Reputation: 1431

As others have stated this is a bit vague without assuming a few things. There are faster, less complicated, ways to this issue but I tried to do my solution as dynamic as possible to suit the vague definition. Here are my assumptions:

Here is the SQL Fiddle: SQL Fiddle Demo

Assumptions

  • Assuming SQL Server 2005+
  • Assuming the shift tables date part is 1900-01-01
  • Assumes the Break table has proper date for StartTime / EndTime
  • Assumes only 1 person in the database clocking in and out (No EmployeeID included in @BREAK table)
  • Assumes the work they entire shift. Clocking in precisely at the shift start and out at the shift end.

Tables

DECLARE @SHIFT Table (ID INT IDENTITY(1,1) PRIMARY KEY, StartTime DATETIME, EndTime DATETIME)
INSERT INTO @SHIFT (StartTime, EndTime) VALUES 
('07:20:00','15:20:00'),
('15:20:00','23:20:00'),
('23:20:00','07:20:00')

DECLARE @BREAK Table (ID INT IDENTITY(1,1) PRIMARY KEY, StartTime DATETIME, EndTime DATETIME)
INSERT INTO @BREAK (StartTime, EndTime) VALUES
('1/1/2013 09:10:00','1/1/2013 09:25:00'),
('1/1/2013 11:30:00','1/1/2013 12:05:00'),
('1/1/2013 13:30:00','1/1/2013 13:45:00'),
('1/1/2013 17:10:00','1/1/2013 17:25:00'),
('1/1/2013 19:30:00','1/1/2013 20:05:00'),
('1/1/2013 21:30:00','1/1/2013 21:45:00'),
('1/2/2013 01:10:00','1/2/2013 01:25:00'),
('1/2/2013 03:30:00','1/2/2013 04:05:00'),
('1/2/2013 05:30:00','1/2/2013 05:45:00'),

('1/2/2013 09:10:00','1/2/2013 09:25:00'),
('1/2/2013 11:30:00','1/2/2013 12:05:00'),
('1/2/2013 13:30:00','1/2/2013 13:45:00'),
('1/2/2013 17:10:00','1/2/2013 17:25:00'),
('1/2/2013 19:30:00','1/2/2013 20:05:00'),
('1/2/2013 21:30:00','1/2/2013 21:45:00'),
('1/2/2013 01:10:00','1/2/2013 01:25:00'),
('1/2/2013 03:30:00','1/2/2013 04:05:00'),
('1/2/2013 05:30:00','1/2/2013 05:45:00')

Solution

;WITH
MinMaxDates AS --FINDS THE MINIMUM AND MAXIMUM DATE RANGES NEEDING SHIFTS ASSOCIATED.
(
    SELECT 
        CAST(MIN(B.StartTime) AS DATE) AS MinDate, 
        CAST(MAX(B.EndTime) AS DATE) AS MaxDate 
    FROM @BREAK AS B
),
RecursiveDateBuilder AS --RECURSIVELY BUILDS A LIST OF DATES BETWEEN THE MINIMUM AND MAXIMUM RANGES IN BREAKS
(
    SELECT MinDate AS ShiftStartDate FROM MinMaxDates
    UNION ALL
    SELECT DATEADD(dd,1,ShiftStartDate) FROM RecursiveDateBuilder WHERE DATEADD(dd,1,ShiftStartDate) <= (SELECT MaxDate FROM MinMaxDates)
),
ShiftSets AS --CREATE A SHIFT SET FOR EVERY DATE
(
    SELECT 
        ROW_NUMBER() OVER (ORDER BY R.ShiftStartDate ASC, S.ID ASC) AS NewShiftID,
        S.ID AS OldShiftID, 
        DATEADD(dd,DATEDIFF(dd,S.StartTime, R.ShiftStartDate),S.StartTime) AS StartDate,
        DATEADD(dd,DATEDIFF(dd,S.EndTime, R.ShiftStartDate),S.EndTime) AS EndDate,
        R.ShiftStartDate AS ShiftGroup
    FROM
        @SHIFT AS S
        CROSS JOIN RecursiveDateBuilder AS R
),
Shifts AS  --FIXES ANY SHIFTS THAT CROSS MIDNIGHT SETTING THEM TO THE NEXT DAY
(
SELECT
    S.NewShiftID AS ShiftID,
    S.StartDate,
    CASE 
        WHEN S.EndDate <= Min2.MinStartDate THEN DATEADD(DAY,1,S.EndDate) 
        ELSE S.EndDate 
    END AS EndDate
FROM
    ShiftSets AS S
    CROSS APPLY (SELECT MIN(Mins.StartDate) AS MinStartDate FROM ShiftSets AS Mins WHERE Mins.ShiftGroup = S.ShiftGroup) AS Min2 
),
BreaksToShifts AS  --ASSOCIATES THE PUNCHES TO THE SHIFTS
(
    SELECT
        B.StartTime AS ClockIn,
        B.EndTime AS ClockOut,
        S.ShiftID,
        S.StartDate,
        S.EndDate
    FROM
        @BREAK AS B
        INNER JOIN Shifts AS S ON (B.StartTime BETWEEN S.StartDate AND S.EndDate AND B.EndTime BETWEEN S.StartDate AND S.EndDate)
),
Punches AS
(
    SELECT ROW_NUMBER() OVER (ORDER BY S.TheTime ASC) AS ID, S.TheTime FROM 
    (
        SELECT BS.ShiftID, BS.ClockIn AS TheTime FROM BreaksToShifts AS BS
        UNION ALL
        SELECT BS.ShiftID, MIN(BS.StartDate) AS TheTime FROM BreaksToShifts AS BS GROUP BY BS.ShiftID
        UNION ALL
        SELECT BS.ShiftID, BS.ClockOut AS TheTime FROM BreaksToShifts AS BS
        UNION ALL
        SELECT BS.ShiftID, MAX(BS.EndDate) AS TheTime FROM BreaksToShifts AS BS GROUP BY BS.ShiftID
    ) AS S
)
SELECT
    *
FROM
    Punches AS P1
    INNER JOIN Punches AS P2 ON (P2.ID = P1.ID + 1)
WHERE
    P1.ID % 2 > 0

Upvotes: 2

Related Questions