如何计算SQL Server中两行列之间的持续时间?

时间:2012-09-20 05:42:28

标签: sql-server

我在数据库中有这样的数据

ID  Server DownTime          ServerStatus
--- -----------------------  ------------
1   2012-03-30 00:00:00.000  1
2   2012-03-30 00:30:00.000  0
3   2012-03-30 01:00:00.000  0
4   2012-03-30 01:30:00.000  0
5   2012-03-30 02:00:00.000  1
6   2012-03-30 02:30:00.000  1
7   2012-03-30 03:00:00.000  0
8   2012-03-30 03:30:00.000  1

我需要一个查询或存储过程,它将输出为

Start Time    EndTime       TotalDownTimeinMinutes
------------  ------------  ----------------------
3/30/12 0:30  3/30/12 2:00  90
3/30/12 3:00  3/30/12 3:30  30

2 个答案:

答案 0 :(得分:2)

-- because each "back up" can relate to multiple "down" times,
-- we take the longest period using MIN
 SELECT Min(ServerDownTime) StartTime,
        UpTime EndTime,
        DateDiff(MI, Min(ServerDownTime), UpTime)
   FROM
(
 SELECT Down.ServerDownTime,
      (-- subquery gives you the time when it came back up
       SELECT Top 1 Up.ServerDownTime
         FROM Tbl Up
        WHERE Up.ServerDownTime > Down.ServerDownTime
          AND Up.ServerStatus=1
     ORDER BY Up.ServerDownTime ASC) UpTime
   FROM Tbl Down
  WHERE Down.ServerStatus=0 -- find all the downs
) X
GROUP BY UpTime
ORDER BY UpTime

您可以使用此DDL

测试上述查询
create table Tbl
(
ID int,
ServerDownTime datetime,
ServerStatus bit
)
insert Tbl select
1   ,'2012-03-30 00:00:00.000',  1 union all select
2   ,'2012-03-30 00:30:00.000',  0 union all select
3   ,'2012-03-30 01:00:00.000',  0 union all select
4   ,'2012-03-30 01:30:00.000',  0 union all select
5   ,'2012-03-30 02:00:00.000',  1 union all select
6   ,'2012-03-30 02:30:00.000',  1 union all select
7   ,'2012-03-30 03:00:00.000',  0 union all select
8   ,'2012-03-30 03:30:00.000',  1

或者,如果您在网络上并且远离SQL Server,那么这里是SQL Fiddle

答案 1 :(得分:0)

此解决方案基于recursive CTE's

DECLARE @MyTable TABLE (
    ID INT PRIMARY KEY,
    ServerDownTime DATETIME NOT NULL,
        UNIQUE (ServerDownTime),
    ServerStatus BIT NOT NULL
);

INSERT  @MyTable (ID, ServerDownTime, ServerStatus)
SELECT 1,'2012-03-30T00:00:00',1 UNION ALL
SELECT 2,'2012-03-30T00:30:00',0 UNION ALL
SELECT 3,'2012-03-30T01:00:00',0 UNION ALL
SELECT 4,'2012-03-30T01:30:00',0 UNION ALL
SELECT 5,'2012-03-30T02:00:00',1 UNION ALL
SELECT 6,'2012-03-30T02:30:00',1 UNION ALL
SELECT 7,'2012-03-30T03:00:00',0 UNION ALL
SELECT 8,'2012-03-30T03:30:00',1;

WITH Base
AS
(
    SELECT  *, ROW_NUMBER() OVER(ORDER BY t.ServerDownTime) AS RowNum
    FROM    @MyTable t
),  DownTimeGrouping
AS
(
    SELECT  crt.RowNum,
            crt.ID,
            crt.ServerDownTime,
            crt.ServerStatus,
            CASE WHEN crt.ServerStatus=0 THEN 1 END AS GroupID,
            CASE WHEN crt.ServerStatus=0 THEN 1 ELSE 0 END AS LastGroupID
    FROM    Base crt
    WHERE   crt.RowNum=1
    UNION ALL
    SELECT  crt.RowNum,
            crt.ID,
            crt.ServerDownTime,
            crt.ServerStatus,
            CASE 
                WHEN prev.ServerStatus=0 AND crt.ServerStatus IN(0,1) THEN prev.GroupID 
                WHEN prev.ServerStatus=1 AND crt.ServerStatus=0 THEN prev.LastGroupID+1
            END AS GroupID,
            CASE 
                WHEN prev.ServerStatus=0 AND crt.ServerStatus IN(0,1) THEN prev.GroupID 
                WHEN prev.ServerStatus=1 AND crt.ServerStatus=0 THEN prev.LastGroupID+1
                WHEN prev.ServerStatus=1 AND crt.ServerStatus=1 THEN prev.GroupID
            END AS LastGroupID
    FROM    Base crt
    INNER JOIN DownTimeGrouping prev ON crt.RowNum=prev.RowNum+1

)
SELECT  *, DATEDIFF(MINUTE,x.StartTime,x.EndTime) AS MinutesDiff
FROM (
    SELECT  t.GroupID, MIN(t.ServerDownTime) AS StartTime, MAX(t.ServerDownTime) AS EndTime
    FROM    DownTimeGrouping t
    WHERE   t.GroupID IS NOT NULL
    GROUP BY t.GroupID
) x

基本思想是对以ServerStatus = 0行开头并以ServerStatus = 1行结束的行进行分组。例如,如果运行此查询,您将看到停机组(列GroupID)::

WITH Base
AS
(...),  DownTimeGrouping
AS
(...)
SELECT  *
FROM    DownTimeGrouping g
ORDER BY g.RowNum

RowNum               ID          ServerDownTime          ServerStatus GroupID     LastGroupID
-------------------- ----------- ----------------------- ------------ ----------- -----------
1                    1           2012-03-30 00:00:00.000 1            NULL        0
2                    2           2012-03-30 00:30:00.000 0            1           1
3                    3           2012-03-30 01:00:00.000 0            1           1
4                    4           2012-03-30 01:30:00.000 0            1           1
5                    5           2012-03-30 02:00:00.000 1            1           1
6                    6           2012-03-30 02:30:00.000 1            NULL        1
7                    7           2012-03-30 03:00:00.000 0            2           2
8                    8           2012-03-30 03:30:00.000 1            2           2