如果某些持续时间比预期的长,我该如何选择这些开始时间和持续时间?

时间:2019-05-22 12:41:33

标签: sql-server tsql

我有一张开始时间和持续时间的表格,其中一些持续时间超过了下一个开始时间。我希望以更正下一个开始时间的方式选择数据-

编辑,但时间不会减少!

我正在SQL Server 2016上运行此查询。我已经尝试使用LEAD和LAG,但无法使其正常工作。

原始表格是

| BeginTime | Duration |
------------------------
| 6:00      |       75 |
| 7:00      |       45 |
| 7:45      |       60 |
| 9:00      |       90 |
| 11:00     |       60 |
| 11:30     |       30 |
------------------------

输出应为

| BeginTime | Duration |
------------------------
| 6:00      |       75 |
| 7:15      |       45 |
| 8:00      |       60 |
| 9:00      |       90 |
| 11:00     |       60 |
| 12:00     |       30 |
------------------------

3 个答案:

答案 0 :(得分:2)

您可以尝试使用一些聚合函数

  1. LAG窗口功能与DATEADD
  2. SUM窗口函数以累积Duration个值。

然后,唯一带有total列值的时间就是来自子查询。

MIN(BeginTime)女巫的最终加法时间是指开始时间。

查询1

SELECT (CASE WHEN rn = 1 THEN BeginTime
            ELSE DATEADD(MINUTE,LAG(total) OVER(ORDER BY BeginTime),MIN(BeginTime) OVER(ORDER BY BeginTime)) END) BeginTime
      ,Duration
FROM (
  SELECT *,
         SUM(Duration) OVER(ORDER BY BeginTime) total,
         ROW_NUMBER() OVER(ORDER BY BeginTime) rn,
         LAG(BeginTime) OVER(ORDER BY BeginTime) privous
  FROM T
) t1

Results

|        BeginTime | Duration |
|------------------|----------|
| 06:00:00.0000000 |       75 |
| 07:15:00.0000000 |       45 |
| 08:00:00.0000000 |       60 |
| 09:00:00.0000000 |       90 |

答案 1 :(得分:1)

  --Sample
  SELECT CAST('6:00' as TIME) as BeginTime, 75 as Duration
  INTO #Temp  --drop table #Temp
  UNION ALL
  SELECT '7:00',45
  UNION ALL
  SELECT '7:15',45
  UNION ALL
  SELECT '7:45',60
  UNION ALL
  SELECT '8:00',60
  UNION ALL
  SELECT '9:00',90


  --Query starts from here
   DECLARE @MinTime TIME  
  SELECT @MinTime = MIN(BeginTime) FROM #Temp

  DECLARE @MaxTime TIME  
  SELECT @MaxTime = MAX(BeginTime) FROM #Temp
  ;
  WITH A(BeginTime, Duration)
  AS
  (
     SELECT BeginTime, Duration FROM #Temp where BeginTime = @MinTime 
     UNION ALL
     SELECT DATEADD(mi,A.Duration,A.BeginTime),B.Duration
     FROM A 
     INNER JOIN #Temp as B
     ON B.BeginTime = DATEADD(mi,A.Duration,A.BeginTime)
     WHERE DATEADD(mi,A.Duration,A.BeginTime) < = @MaxTime
  )
  SELECT * FROM A

编辑答案:

在这种情况下,所有其他BeginTime都是无用的,您需要总结的是Duration与第一个BeginTime的总数:

 SELECT CAST('6:00' as TIME) as BeginTime, 75 as Duration, 1 as [Count]
  INTO #Temp  --drop table #Temp
  UNION ALL
  SELECT '7:00',45, 2
  UNION ALL
  SELECT '8:00',60, 3
  UNION ALL
  SELECT '9:00',90,4
  UNION ALL
  SELECT '10:00',60,5
  UNION ALL
  SELECT '11:00',30,6


DECLARE @MinTime Time
SELECT @MinTime =  MIN(BeginTime) FROM #Temp

SELECT BeginTime,Duration FROM #Temp WHERE BeginTime = @MinTime

UNION ALL

SELECT DATEADD(MI,SUM(B.Duration), @MinTime) as BeginTime,
       A.Duration
FROM #Temp as A
INNER JOIN #Temp as B
ON B.[Count] < A.[Count]
WHERE A.BeginTime != @MinTime
GROUP BY A.Duration, A.[Count]
ORDER BY BeginTime

答案 2 :(得分:0)

我的一个朋友能够解决问题-这是解决方案:

--DROP TABLE #a;
CREATE TABLE #a
(
    BeginTime DATETIME,
    Duration INT,
    BeginTimeNew DATETIME
);

INSERT INTO #a
(
    BeginTime,
    Duration
)
VALUES
('20190828  8:00:00', 75),
('20190828  9:00:00', 30),
('20190828 10:00:00', 45),
('20190828 11:00:00', 90),
('20190828 12:00:00', 75),
('20190828 13:00:00', 15),
('20190828 14:00:00', 75),
('20190828 15:00:00', 60);

SELECT *
FROM #a
ORDER BY BeginTime,
         Duration


;
WITH rownumber
AS (SELECT r = ROW_NUMBER() OVER (ORDER BY BeginTime, Duration),
           BeginTime,
           Duration
    FROM #a),
     cte
AS (SELECT r,
           OrigBeginTime = BeginTime,
           Duration,
           LV = 1,
           BeginTime,
           EndTime = DATEADD(minute, Duration, BeginTime)
    FROM rownumber
    WHERE r = 1
    UNION ALL
    SELECT a.r,
           a.BeginTime,
           a.Duration,
           b.LV + 1,
           BeginTime = IIF(b.EndTime >= a.BeginTime, b.EndTime, a.BeginTime),
           EndTime = DATEADD(minute, a.Duration, IIF(b.EndTime >= a.BeginTime, b.EndTime, a.BeginTime))
    FROM rownumber a
        INNER JOIN cte b
            ON a.r = b.r + 1)
UPDATE a
SET BeginTimeNew = b.BeginTime
FROM #a a
    INNER JOIN cte b
        ON a.BeginTime = b.OrigBeginTime
           AND a.Duration = b.Duration
OPTION (MAXRECURSION 0);

SELECT *
FROM #a
ORDER BY BeginTime,
         Duration