简体   繁体   English

在SQL中跨多行汇总日期?

[英]Summing dates across multiple rows in SQL?

We have a Table that stores alarms for certain SetPoints in our system. 我们有一个表,用于存储系统中某些设定点的警报。 I'm attempting to write a query that first gets the difference between two dates (spread across two rows), and then sums all of the date differences to get a total sum for the amount of time the setpoint was in alarm. 我正在尝试编写一个查询,该查询首先获取两个日期之间的差(分布在两行中),然后将所有日期差求和,以求出设定值处于警报状态的总时间。

We have one database where I've accomplished similar, but in that case, both the startTime and endTime were in the same row. 我们有一个数据库,我已经完成了类似的工作,但是在那种情况下,startTime和endTime都在同一行中。 In this case, this is not adequate 在这种情况下,这是不够的

Some example Data 一些示例数据

|   Row   |   TagID   |   SetPointID   |         EventLogTime         |   InAlarm   |
-------------------------------------------------------------------------------------
|    1    |     1     |        2       |    2016-01-01 01:49:18.070   |      1      |
|    2    |     1     |        1       |    2016-01-01 03:23:39.970   |      1      |
|    3    |     1     |        2       |    2016-01-01 03:23:40.070   |      0      |
|    4    |     1     |        1       |    2016-01-01 08:04:01.260   |      0      |
|    5    |     1     |        2       |    2016-01-01 08:04:01.370   |      1      |
|    6    |     1     |        1       |    2016-01-01 11:40:36.367   |      1      |
|    7    |     1     |        2       |    2016-01-01 11:40:36.503   |      0      |
|    8    |     1     |        1       |    2016-01-01 13:00:30.263   |      0      |

Results 结果

|   TagID    |   SetPointID   |   TotalTimeInAlarm   |
------------------------------------------------------
|     1      |        1       |    6.004443  (hours) |
|     1      |        2       |    5.182499  (hours) |

Essentially, what I need to do is to get the start time and end time for each tag and each setpoint, then I need to get the total time in alarm. 本质上,我需要做的是获取每个标签和每个设定点的开始时间和结束时间,然后我需要获取警报中的总时间。 I'm thing CTEs might be able to help, but I'm not sure. 我认为CTE可以提供帮助,但是我不确定。

I believe the pseudo query logic would be similar to 我相信伪查询逻辑将类似于

Define @startTime DATETIME, @endTime DATETIME

SELECT TagID,
       SetPointID,
       ABS(First Occurrence of InAlarm = True (since last occurrence WHERE InAlarm = False) 
           - First Occurrence of InAlarm = False (since last occurrence WHERE InAlarm = True)) 
       -- IF no InAlarm = False use @endTime.
GROUP BY TagID, SetPointID

You can use the LEAD windowed function (or LAG ) to do this pretty easily. 您可以使用LEAD窗口功能(或LAG )轻松完成此操作。 This assumes that the rows always come in pairs with 1-0-1-0 for "InAlarm". 假设“ InAlarm”的行始终与1-0-1-0成对出现。 If that doesn't happen then it's going to throw things off. 如果那没有发生,那将丢掉东西。 You would need to have business rules for these situations in any event. 无论如何,您都需要针对这些情况制定业务规则。

;WITH CTE_Timespans AS
(
    SELECT
        TagID,
        SetPointID,
        InAlarm,
        EventLogTime,
        LEAD(EventLogTime, 1) OVER (PARTITION BY TagID, SetPointID ORDER BY EventLogTime) AS EndingEventLogTime
    FROM
        My_Table
)
SELECT
    TagID,
    SetPointID,
    SUM(DATEDIFF(SS, EventLogTime, EndingEventLogTime))/3600.0 AS TotalTime
FROM
    CTE_Timespans
WHERE
    InAlarm = 1
GROUP BY
    TagID,
    SetPointID

One easy way is to use OUTER APPLY to get the next date that is not InAlarm 一种简单的方法是使用OUTER APPLY获取不是InAlarm的下一个日期

SELECT  mt.TagID,
        mt.SetPointID,
        SUM(DATEDIFF(ss,mt.EventLogTime,oa.EventLogTime)) / 3600.0 AS [TotalTimeInAlarm]
FROM    MyTable mt
        OUTER APPLY (SELECT MIN([EventLogTime]) EventLogTime
                     FROM   MyTable mt2
                     WHERE  mt.TagID = mt2.TagID
                            AND mt.SetPointID = mt2.SetPointID
                            AND mt2.EventLogTime > mt.EventLogTime
                            AND InAlarm = 0
                    ) oa
WHERE   mt.InAlarm = 1
GROUP BY mt.TagID,
        mt.SetPointID

LEAD() might perform better if using MSSQL 2012+ 如果使用MSSQL 2012+,则LEAD()可能更高

In SQL Server 2014+: 在SQL Server 2014+中:

SELECT  tagId, setPointId, SUM(DATEDIFF(second, pt, eventLogTime)) / 3600. AS diff
FROM    (
        SELECT  *,
                LAG(inAlarm) OVER (PARTITION BY tagId, setPointId ORDER BY eventLogTime, row) ppa,
                LAG(eventLogTime) OVER (PARTITION BY tagId, setPointId ORDER BY eventLogTime, row) pt
        FROM    (
                SELECT  LAG(inAlarm) OVER (PARTITION BY tagId, setPointId ORDER BY eventLogTime, row) pa,
                        *
                FROM    mytable
                ) q
        WHERE   EXISTS
                (
                SELECT  pa
                EXCEPT
                SELECT  inAlarm
                )
        ) q
WHERE   ppa = 0
        AND inAlarm = 1
GROUP BY
        tagId, setPointId

This will filter out consecutive events with same alarm state 这将过滤出具有相同警报状态的连续事件

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM