来自大表的运行时摘要

时间:2014-12-11 04:44:03

标签: sql tsql sql-server-2008-r2

我有一个包含5亿行的表,以及25,000个唯一的tag_ids,它看起来像:

tag_id  event_time  event_value reason_type
10087   2011-01-01 04:31:28.000 0   NULL
10087   2011-01-01 18:03:28.000 0   NULL
10087   2011-01-02 07:35:27.000 1   NULL
10087   2011-01-02 21:07:27.000 0   NULL
10087   2011-01-03 10:39:27.000 1   NULL
10087   2011-01-04 00:11:27.000 1   NULL

对于特定的tag_id,0代表电机关闭,1代表电机开启。

系统会在随机时间或状态发生变化时轮询电机状态。

我想做一个摘要,显示电机运行的时间。像:

tag_id  date runtime_mins
10087 2011-01-04 3600
10087 2011-01-05 2456
10087 2011-01-06 2321

感谢您的想法和帮助!

2 个答案:

答案 0 :(得分:0)

DECLARE @tmp AS TABLE
        (
          tag_id INT ,
          event_time DATETIME ,
          event_value INT
        )
    INSERT  INTO @tmp
    VALUES  ( 10087, '2011-01-01 04:31:28.000', 1 ),
            ( 10087, '2011-01-01 10:31:28.000', 0 ),
            ( 10087, '2011-01-01 18:03:28.000', 1 ),
            ( 10087, '2011-01-02 07:35:27.000', 1 ),
            ( 10087, '2011-01-02 21:07:27.000', 0 ),
            ( 10087, '2011-01-03 10:39:27.000', 1 ),
            ( 10087, '2011-01-04 00:11:27.000', 1 )
    ------------------------------------------------------------------
    --create temp table for data 'turned on'
    IF OBJECT_ID('Tempdb..#Turnedon') IS NOT NULL 
        DROP TABLE #Turnedon
    SELECT  T.tag_id ,
            CONVERT(DATE, T.event_time) AS Edate ,
            CONVERT(TIME, T.event_time) AS Etime ,
            ROW_NUMBER() OVER ( PARTITION BY T.tag_id, CONVERT(DATE, T.event_time) ORDER BY T.event_time ) RN
    INTO    #Turnedon
    FROM    @tmp AS T
    WHERE   T.event_value = 1
    ------------------------------------------------------------------
    --create temp table for data 'turned off'
    IF OBJECT_ID('Tempdb..#Turnedoff') IS NOT NULL 
        DROP TABLE #Turnedoff
    SELECT  T.tag_id ,
            CONVERT(DATE, T.event_time) AS Edate ,
            CONVERT(TIME, T.event_time) AS Etime ,
            ROW_NUMBER() OVER ( PARTITION BY T.tag_id, CONVERT(DATE, T.event_time) ORDER BY T.event_time ) RN
    INTO    #Turnedoff
    FROM    @tmp AS T
    WHERE   T.event_value = 0
    ------------------------------------------------------------------
    --Create temp table for catalog with unique dates and tag_id
    IF OBJECT_ID('Tempdb..#Catalog') IS NOT NULL 
        DROP TABLE #Catalog
    SELECT DISTINCT
            T.tag_id ,
            CONVERT(DATE, T.event_time) AS Edate
    INTO    #Catalog
    FROM    @tmp AS T
    -------------------------------------------------------------------
/* 
row number helps to determine if on-off was done more than one time. 
so, if on-off was done more than once then you can aggregate (runtime) final result.
but also, it's better before aggregation insert data in temp table due to huge data amount
*/

    SELECT  C.tag_id ,
            C.Edate ,
            COALESCE(T.RN, 1) AS [event id] ,
            COALESCE(T.Etime, CONVERT(TIME, '00:00:00')) AS [turned on] ,
            COALESCE(T2.Etime, CONVERT(TIME, '23:59:59')) AS [turned off] ,
            DATEDIFF(MINUTE, COALESCE(T.Etime, CONVERT(TIME, '00:00:00')),
                     COALESCE(T2.Etime, CONVERT(TIME, '23:59:59'))) Runtime
    FROM    #Catalog AS C
            LEFT JOIN #Turnedon AS T ON C.tag_id = T.tag_id
                                        AND C.Edate = T.Edate
            LEFT JOIN #Turnedoff AS T2 ON C.tag_id = T2.tag_id
                                          AND C.Edate = T2.Edate
                                          AND COALESCE(T.RN, 1) = T2.RN

Snapshot with Values updated

答案 1 :(得分:0)

--in case when something like "off-off-on-off-of-on", just example of data normalization
 DECLARE    @tmp AS TABLE
        (
          tag_id INT ,
          event_time DATETIME ,
          event_value INT
        )
    INSERT  INTO @tmp
    VALUES  ( 10087, '2011-01-01 04:31:28.000', 1 ),
            ( 10087, '2011-01-01 10:31:28.000', 0 ),
            ( 10087, '2011-01-01 18:03:28.000', 1 ),
            ( 10087, '2011-01-02 02:35:27.000', 1 ),
            ( 10087, '2011-01-02 07:35:27.000', 1 ),
            ( 10087, '2011-01-02 11:07:27.000', 0 ),
            ( 10087, '2011-01-02 21:07:27.000', 0 )
    ,       ( 10087, '2011-01-03 10:39:27.000', 1 ),
            ( 10087, '2011-01-04 00:11:27.000', 1 )
        ------------------------------------------------------------------
        --create temp table for data 'turned on'
    IF OBJECT_ID('Tempdb..#Turnedon') IS NOT NULL 
        DROP TABLE #Turnedon
    SELECT  T.tag_id ,
            T.Edate ,
            ( CASE WHEN t.event_value = 0
                        AND t.prev_event_value = 0 THEN t.prev_Etime
                   ELSE t.Etime
              END ) AS Etime ,
            ( CASE WHEN t.event_value = 0
                        AND t.prev_event_value = 0
                   THEN 'previous ''off'' was not executed'
                   ELSE NULL
              END ) AS event_desc ,
            ROW_NUMBER() OVER ( PARTITION BY T.tag_id, Edate ORDER BY Etime ) RN
    INTO    #Turnedon
    FROM    ( SELECT    T.tag_id ,
                        CONVERT(DATE, T.event_time) AS Edate ,
                        CONVERT(TIME, T.event_time) AS Etime ,
                        T.event_value ,
                        lag(T.event_value) OVER ( PARTITION BY T.tag_id,
                                                  CONVERT(DATE, T.event_time) ORDER BY T.event_time ) AS prev_event_value ,
                        lag(CONVERT(TIME, T.event_time)) OVER ( PARTITION BY T.tag_id,
                                                                CONVERT(DATE, T.event_time) ORDER BY T.event_time ) AS prev_Etime
              FROM      @tmp AS T
            ) T
    WHERE   T.event_value = 1
            OR ( T.event_value = 0
                 AND T.prev_event_value = 0
               )
        ------------------------------------------------------------------
        --create temp table for data 'turned off'
    IF OBJECT_ID('Tempdb..#Turnedoff') IS NOT NULL 
        DROP TABLE #Turnedoff
    SELECT  T.tag_id ,
            T.Edate ,
            ( CASE WHEN t.event_value = 1
                        AND t.prev_event_value = 1 THEN t.prev_Etime
                   ELSE t.Etime
              END ) AS Etime ,
            ( CASE WHEN t.event_value = 1
                        AND t.prev_event_value = 1
                   THEN '''on'' was not executed'
                   ELSE NULL
              END ) AS event_desc ,
            ROW_NUMBER() OVER ( PARTITION BY T.tag_id, Edate ORDER BY Etime ) RN
    INTO    #Turnedoff
    FROM    ( SELECT    T.tag_id ,
                        CONVERT(DATE, T.event_time) AS Edate ,
                        CONVERT(TIME, T.event_time) AS Etime ,
                        T.event_value ,
                        lag(T.event_value) OVER ( PARTITION BY T.tag_id,
                                                  CONVERT(DATE, T.event_time) ORDER BY T.event_time ) AS prev_event_value ,
                        lag(CONVERT(TIME, T.event_time)) OVER ( PARTITION BY T.tag_id,
                                                                CONVERT(DATE, T.event_time) ORDER BY T.event_time ) AS prev_Etime
              FROM      @tmp AS T
            ) T
    WHERE   T.event_value = 0
            OR ( T.event_value = 1
                 AND T.prev_event_value = 1
               )                                     
        ------------------------------------------------------------------
        --Create temp table for catalog with unique dates and tag_id
    IF OBJECT_ID('Tempdb..#Catalog') IS NOT NULL 
        DROP TABLE #Catalog
    SELECT DISTINCT
            T.tag_id ,
            CONVERT(DATE, T.event_time) AS Edate
    INTO    #Catalog
    FROM    @tmp AS T
        -------------------------------------------------------------------
    /* 
    row number helps to determine if on-off was done more than one time. 
    so, if on-off was done more than once then you can aggregate (runtime) final result.
    but also, it's better before aggregation insert data in temp table due to huge data amount
    */       
    SELECT  C.tag_id ,
            C.Edate ,
            COALESCE(T.RN, 1) AS [event id] ,
            COALESCE(T.Etime, CONVERT(TIME, '00:00:00')) AS [turned on] ,
            COALESCE(T2.Etime, CONVERT(TIME, '23:59:59')) AS [turned off] ,
            DATEDIFF(MINUTE, COALESCE(T.Etime, CONVERT(TIME, '00:00:00')),
                     COALESCE(T2.Etime, CONVERT(TIME, '23:59:59'))) Runtime ,
            COALESCE(t2.event_desc, t.event_desc, '') AS event_desc
    FROM    #Catalog AS C
            LEFT JOIN #Turnedon AS T ON C.tag_id = T.tag_id
                                        AND C.Edate = T.Edate
            LEFT JOIN #Turnedoff AS T2 ON C.tag_id = T2.tag_id
                                          AND C.Edate = T2.Edate
                                          AND COALESCE(T.RN, 1) = T2.RN

enter image description here