Sql 根据轮班和休息表计算工作时间

Sql 根据轮班和休息表计算工作时间,sql,sql-server-2008,tsql,Sql,Sql Server 2008,Tsql,我正在使用SQLServer2008。我有两个表移位和中断,数据如下: 每周轮班7天 移位表中的数据 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 中断表中的数据 ID Desc Start_Time End_Time 1 1st Shift - 1st break

我正在使用SQLServer2008。我有两个表移位和中断,数据如下: 每周轮班7天

移位表中的数据

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
中断表中的数据

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
输出需要类似于:

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
....

我是新加入的,有人能帮我加入吗。另外,如果您认为表结构中的任何更改都会有所帮助,请提出建议。

正如其他人所说,这有点模糊,没有假设一些事情。解决这个问题有更快、更简单的方法,但我试图尽可能动态地解决问题,以适应模糊的定义。以下是我的假设:

下面是SQL小提琴:

假设

假设SQL Server 2005+ 假设班次表日期部分为1900-01-01 假设中断表具有正确的开始时间/结束时间日期 假设数据库中只有一个人打卡进出没有 @BREAK表中包含的员工ID 承担他们整个班次的工作。准时上班 在轮班结束时开始并离开。 桌子

解决方案


这不是小事,特别是考虑到第三班的时钟超过了24小时的界限:-StuartLC是对的,24小时的界限是多一点工作。但是,在回答之前:是每周七天轮班还是你错过了一周中的哪一天?手工操作非常简单,因此如果您输入一周中的几天,甚至是确切的日期,解决方案可能会有所不同。每周轮班7天。您能将数据转储到类似C的地方并在那里进行处理吗?SQL不是为这种类型的工作而设计的…@AbeMiessler SQL完全可以完成这种类型的工作。它需要在集合中思考。+1因为我喜欢使用CTE来克服原始表格结构的限制。感谢SQL FIDLE—它总是有助于分析代码。
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')
;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