我有一个包含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
感谢您的想法和帮助!
答案 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
答案 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