在SQL中拆分重叠日期

时间:2014-04-18 00:10:50

标签: sql date datetime sql-server-2008-r2

我在SQLServer 2008 R2上

我试图为给定期间(通常为30-90天)制造资源的活动创建报告和图表

根据运行时间(例如4天)创建作业。如果周末没有工作并且上述工作在星期五开始,那么资源的活动需要显示1天运行,2天停止,3天运行,而生产调度程序不必进行两项工作。我有工作'一个表中的计划和另一个表中的停机时间(因此将DT视为某种日历表)。不同寻常的是,结束时间由停机因素提供。

所以我需要查询为这个作业创建3个日期时间范围:Fri跑步,周六,太阳下山,周一,周二,周三跑步。注意:单个作业可能有多个停机事件。

已经围绕这个圈了一会儿。我确信这是一种优雅的方式:我无法找到它。我发现了几个类似的帖子,但不能适用于我的案例(或至少可以让他们工作)

以下是一些示例日期和预期结果。我希望解释和示例数据是清楚的。

-- Create tables to work with / Source and Destination
CREATE TABLE #Jobs
    (
     ResourceID int
    ,JobNo VARCHAR(10)
    ,startdate SMALLDATETIME
    ,enddate SMALLDATETIME
    )


CREATE TABLE #Downtime
    (
     ResourceID INT
    ,Reason VARCHAR(10)
     ,startdate SMALLDATETIME
    ,enddate SMALLDATETIME
    )



CREATE TABLE #Results
    (
    ResourceID INT
    ,Activity VARCHAR(10)
    ,startdate SMALLDATETIME
    ,enddate SMALLDATETIME
    ,ActivityType  varchar(1)
    )


-- Job Schedule
INSERT INTO [#Jobs] 
(
[ResourceID],
[JobNo],
startdate
,enddate
)
SELECT 1, 'J1', '2014-04-01 08:00' ,'2014-04-01 17:00'
UNION ALL
SELECT 1, 'J2', '2014-04-01 17:00' , '2014-04-01 23:00'
UNION ALL
SELECT 2, 'J3', '2014-04-01 08:00' ,'2014-04-01 23:00'
UNION ALL
SELECT 3, 'J4', '2014-04-01 08:00' ,'2014-04-01 09:00'

SELECT * FROM #jobs


-- Downtime Scehdule
INSERT INTO [#Downtime] 
(
[ResourceID],
Reason,
startdate
,enddate
)
SELECT  1, 'DOWN', '2014-04-01 10:00' ,'2014-04-01 11:00'
UNION ALL
SELECT  1, 'DOWN', '2014-04-01 21:00' , '2014-04-01 22:00'
UNION ALL
SELECT  2, 'DOWN', '2014-04-01 10:00' ,'2014-04-01 11:00'
UNION ALL
SELECT  2, 'DOWN',  '2014-04-01 21:00' , '2014-04-01 22:00'
 UNION ALL
SELECT  3, 'DOWN', '2014-04-01 10:00' ,'2014-04-01 11:00'
UNION ALL
SELECT  3, 'DOWN',  '2014-04-01 21:00' , '2014-04-01 22:00'



SELECT * FROM #Downtime

-- Expected Results
INSERT INTO [#Results] 
(
Activity,
[ResourceID],
startdate
,enddate
,[ActivityType]
)
SELECT 'J1', 1, '2014-04-01 08:00' ,'2014-04-01 10:00', 'P'
UNION ALL
SELECT 'DOWN', 1, '2014-04-01 10:00' , '2014-04-01 11:00', 'D'
UNION ALL
SELECT 'J1', 1, '2014-04-01 11:00' ,'2014-04-01 17:00', 'P'
UNION ALL
SELECT 'J2', 1, '2014-04-01 17:00' , '2014-04-01 21:00', 'P'
UNION ALL
SELECT 'DOWN', 1, '2014-04-01 21:00' , '2014-04-01 22:00', 'D'
UNION ALL
SELECT 'J2', 1, '2014-04-01 22:00' ,'2014-04-01 23:00', 'P'
UNION ALL
SELECT 'J3', 2, '2014-04-01 08:00' ,'2014-04-01 10:00', 'P'
UNION ALL
SELECT 'DOWN', 2, '2014-04-01 10:00' , '2014-04-01 11:00', 'D'
UNION ALL
SELECT 'J3', 2, '2014-04-01 11:00' ,'2014-04-01 21:00', 'P'
UNION ALL
SELECT 'DOWN', 2, '2014-04-01 21:00' , '2014-04-01 22:00', 'D'
UNION ALL
SELECT 'J3', 2, '2014-04-01 22:00' ,'2014-04-01 23:00', 'P'
 UNION ALL
SELECT 'J4', 3, '2014-04-01 08:00' ,'2014-04-01 09:00', 'P'
UNION ALL
SELECT 'DOWN', 3, '2014-04-01 10:00' , '2014-04-01 11:00', 'D'
UNION ALL
SELECT 'DOWN', 3, '2014-04-01 21:00' , '2014-04-01 22:00', 'D'


SELECT * FROM #Results
ORDER BY [ResourceID], Startdate


DELETE FROM  #Results
|--------------------------J1------------------------------------| running
       |----D1-----|               |-------D2-------|              down
|--J1--|----D1-----|-------J1------|-------D2-------|-----J1-----| result
|-----------------------------J1-----------|                     running
                                                |----D1-------|  down
|-----------------J1-----------------------|    |----D1-------|  result

有人能指出我正确的方向吗?

这是我最接近的。当存在重叠时工作得很好,但是在J4工作在停机之前结束时失败

WITH    cte
      AS ( SELECT
            ROW_NUMBER() OVER ( ORDER BY ResourceID, dt ) AS Rno
           ,x.ResourceID
           ,x.Activity
           ,Dt
           ,xdt.ActivityType
           FROM
           (
            SELECT     
                ResourceID
               ,JobNo AS Activity
               ,startdate
               ,enddate 
               ,'P' AS ActivityType
               FROM #Jobs
            UNION ALL
            SELECT     
                ResourceID
               ,Reason AS Activity
               ,startdate
               ,enddate 
               ,'D' AS ActivityType
               FROM #Downtime 
             ) AS x
            CROSS APPLY 
            ( 
                VALUES ( x.startdate, x.ActivityType),
                        ( x.enddate, x.ActivityType) ) AS xdt 
                ( Dt, ActivityType )
         )

SELECT
    x.ResourceID
   ,CASE WHEN x.Activity > x1.Activity THEN x.Activity
         ELSE x1.Activity
    END AS Activity
   ,x.dt AS StartDate
   ,x1.Dt AS EndDate
   ,CASE WHEN x.ActivityType > x1.ActivityType THEN x.ActivityType
         ELSE x1.ActivityType
    END AS activitytype
FROM
    cte AS x
    LEFT OUTER JOIN cte AS x1 ON x.ResourceID = x1.ResourceID
                                 AND x.Rno = x1.Rno - 1
WHERE
    x1.Dt IS NOT NULL
    AND x1.Dt <> x.Dt;

由于

标记

1 个答案:

答案 0 :(得分:1)

你实际上非常接近 - 而不是在初始CTE中做所有事情,你实际上想要稍后再加入原始数据。基本上,您正在执行变体on the answer supplied here 以下查询可以满足您的需求:

WITH AllDates AS (SELECT a.*, ROW_NUMBER() OVER(PARTITION BY resourceId ORDER BY rangeDate) AS rn
                  FROM (SELECT resourceId, startDate
                        FROM Jobs
                        UNION ALL
                        SELECT resourceId, endDate
                        FROM Jobs
                        UNION ALL
                        SELECT resourceId, startDate
                        FROM Downtime
                        UNION ALL
                        SELECT resourceId, endDate
                        FROM DownTime) a(resourceId, rangeDate)),

 Range AS (SELECT startRange.resourceId,
                  startRange.rangeDate AS startDate, endRange.rangeDate AS endDate
           FROM AllDates startRange
           JOIN AllDates endRange
             ON endRange.resourceId = startRange.resourceId
                AND endRange.rn = startRange.rn + 1
                AND endRange.rangeDate <> startRange.rangeDate)

SELECT Range.resourceId, Range.startDate, Range.endDate, 
       COALESCE(Downtime.reason, Jobs.jobNo) as activity
FROM Range
LEFT JOIN Jobs
       ON Jobs.resourceId = Range.resourceId
          AND Jobs.startDate <= Range.startDate
          AND Jobs.endDate >= Range.endDate
LEFT JOIN Downtime
       ON Downtime.resourceId = Range.resourceId
          AND Downtime.startDate <= Range.startDate
          AND Downtime.endDate >= Range.endDate
WHERE Jobs.jobNo IS NOT NULL
      OR Downtime.reason IS NOT NULL

(和working fiddle。这实际上应该是ANSI标准的SQL) ......产生了预期的结果:

RESOURCEID   STARTDATE             ENDDATE               ACTIVITY
----------------------------------------------------------------------------
1            2014-04-01 08:00:00   2014-04-01 10:00:00   J1 
1            2014-04-01 10:00:00   2014-04-01 11:00:00   DOWN 
1            2014-04-01 11:00:00   2014-04-01 17:00:00   J1 
1            2014-04-01 17:00:00   2014-04-01 21:00:00   J2 
1            2014-04-01 21:00:00   2014-04-01 22:00:00   DOWN 
1            2014-04-01 22:00:00   2014-04-01 23:00:00   J2 
2            2014-04-01 08:00:00   2014-04-01 10:00:00   J3 
2            2014-04-01 10:00:00   2014-04-01 11:00:00   DOWN 
2            2014-04-01 11:00:00   2014-04-01 21:00:00   J3 
2            2014-04-01 21:00:00   2014-04-01 22:00:00   DOWN 
2            2014-04-01 22:00:00   2014-04-01 23:00:00   J3 
3            2014-04-01 08:00:00   2014-04-01 09:00:00   J4 
3            2014-04-01 10:00:00   2014-04-01 11:00:00   DOWN 
3            2014-04-01 21:00:00   2014-04-01 22:00:00   DOWN