【问题标题】:splitting overlapping dates in SQL在 SQL 中分割重叠的日期
【发布时间】:2014-04-18 00:10:49
【问题描述】:

我使用的是 SQLServer 2008 R2

我正在尝试为特定时期(通常为 30-90 天)内制造资源的活动创建报告和图表

在运行期间(例如 4 天)创建作业。如果周末没有工作并且上述作业从星期五开始,则资源的活动需要显示运行 1 天、停运 2 天、运行 3 天,而生产调度程序不必使其成为两个作业。我在一张表中有工作的时间表,在另一张表中有停机时间(所以把 DT 想象成某种日历表)。不同寻常的是,结束时间会包含停机时间。

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

在这个问题上兜圈子已经有一段时间了。我敢肯定有一种优雅的方法可以做到这一点:我就是找不到。我找到了几篇类似的帖子,但无法将任何内容应用于我的案例(或者至少无法让它们工作)

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

-- 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---------- --------------|跑步 |----D1-----| |-------D2--------|向下 |--J1----|----D1-----|--------J1------|-------D2-------|- ----J1-----|结果 |-----------------------------J1------------|跑步 |----D1--------|向下 |-----------------J1----------------------| |----D1--------|结果

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

这是我最接近的。当有重叠时效果很好,但在 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;

谢谢

标记

【问题讨论】:

  • 您的数据库是哪个平台和版本(尽管它可能是 SQL Server 发行版之一,是哪一个)?您提到日期,但您的数据仅在时间上有所不同,是什么?您是想将INSERT 数据转换为#Jobs#Downtime,还是只是构建报告?日历表在这里将非常有用 - 你有一个,对吗?到目前为止,您尝试了哪些无效的方法?不过,感谢您很好地设置开始/结果数据。 (作为旁注,结束日期几乎总是派生信息,即其他状态开始的瞬间)。
  • @Clockwork-Muse 感谢您的回复。不敢相信我忘了提到平台。到目前为止,我已经尝试使用多个 CTE,将所有内容组合到一个表中 - 它们都没有接近,所以不认为值得发布
  • 忘了说结束时间了。你是对的,通常你必须根据开始持续时间和停机时间来计算。但在这种情况下,结束时间是考虑到停机时间的。几乎就像它的历史数据一样:J1 在时间 A 开始并在时间 B 结束(包括停机时间)。停机时间分别记录
  • 好吧,您尝试了哪些没有成功的方法,它是如何让您失败的?我仍然很困惑您是尝试为这两个表构建条目,还是只需要一个 SELECT 语句(本质上是构建 #Results)。
  • @Clockwork-Muse。我添加了我的最新结果和失败的详细信息。我有两个表(工作和停机时间)并尝试构建结果(即给定日期范围内 ResourceX 上的活动——结果在我上面的“图片”中)

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


【解决方案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 

【讨论】:

猜你喜欢
  • 2020-06-22
  • 1970-01-01
  • 1970-01-01
  • 2021-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 2010-11-26
相关资源
最近更新 更多