如何从具有基于时间的数字值的表中获取时间范围?

时间:2016-04-04 14:08:26

标签: c# sql sql-server entity-framework

我有一个(SQL Server)数据库表,其中包含设备的活动日志。该表由DeviceId,时间戳(DateTime)和值字段组成。无论何时打开或关闭(值为1或0),设备都会将其状态更改写入数据库。

现在我想知道什么是获得"活动块的最快方法"从那张桌子。那是什么意思?我希望获得由" 1"定义的所有时间段。价值及其随后的" 0"给定DeviceId的值,以便得到这样的时间范围列表(对于活动块,非活动时间将介于0值后跟1之间):

DateTime ActiveStart, DateTime ActiveEnd

我目前最终首先获得EF作为列表的所有条目,然后循环遍历它们并将每个条目与其前任进行比较,以检查设备是否已打开和关闭。

确实工作,但我确实认为必须有一种更好,更高效的方法。最好的方法是什么?可以使用纯SQL查询(我可以从中构建存储过程)或LINQ to SQL查询。

感谢您的想法和意见!

3 个答案:

答案 0 :(得分:2)

--------------------------
------ sample data -------
--------------------------
declare @t table 
(
    DeviceId int,
    Timestamp DateTime,
    Value bit
)

insert into @t values
(1, '2016-01-01', 1),
(1, '2016-01-05', 1),
(1, '2016-01-07', 1),
(1, '2016-01-08', 0),
(1, '2016-01-10', 0),
(1, '2016-01-21', 0),
(1, '2016-01-22', 1),
(1, '2016-01-25', 0),
(2, '2016-01-02', 1),
(2, '2016-01-04', 0),
(2, '2016-01-06', 1),
(2, '2016-01-08', 0),
(2, '2016-01-09', 1),
(2, '2016-01-15', 0),
(2, '2016-01-18', 1)

--------------------------
---------- query ---------
--------------------------

select
    DeviceId,
    gr,
    ActiveStart = max(case when Value = 1 then Timestamp end),
    ActiveEnd = max(case when Value = 0 then Timestamp end)
from
(
    select 
        *,
        gr = Value + row_number() over(partition by DeviceId order by Timestamp)
    from @t
) t
group by DeviceId, gr
-- optional sorting by dates for easier results evaluation:
--order by DeviceId,
--  case when max(case when value = 1 then Timestamp end) is NULL
--  then max(case when value = 0 then Timestamp end)
--  else max(case when value = 1 then Timestamp end) end

答案 1 :(得分:1)

您可以这样尝试:

CREATE TABLE #deviceLog (DeviceID INT, Activity DATETIME,Stat INT);
INSERT INTO #deviceLog VALUES
 (1,{ts'2016-04-04 11:20:00'},1)
,(1,{ts'2016-04-04 11:30:00'},0)
,(1,{ts'2016-04-04 11:33:00'},1)
,(1,{ts'2016-04-04 11:38:00'},0)
,(2,{ts'2016-04-04 12:33:00'},1)
,(2,{ts'2016-04-04 12:40:00'},0)
,(3,{ts'2016-04-04 10:33:00'},1)
,(3,{ts'2016-04-04 11:38:00'},0);

WITH AllOn AS
(
    SELECT ROW_NUMBER() OVER(PARTITION BY DeviceID ORDER BY Activity) AS Inx,*
    FROM #deviceLog
    WHERE Stat=1
)
,AllOff AS
(
    SELECT ROW_NUMBER() OVER(PARTITION BY DeviceID ORDER BY Activity) AS Inx,*
    FROM #deviceLog
    WHERE Stat=0
)
SELECT AllOn.*,AllOff.Activity AS OffActivity
FROM AllOn
INNER JOIN AllOff ON AllOn.DeviceID=AllOff.DeviceID AND AllOn.Inx=AllOff.Inx;

DROP TABLE #deviceLog;

结果

Inx DeviceID    Activity          Stat  OffActivity
 1     1    2016-04-04 11:20:00.000 1   2016-04-04 11:30:00.000
 2     1    2016-04-04 11:33:00.000 1   2016-04-04 11:38:00.000
 1     2    2016-04-04 12:33:00.000 1   2016-04-04 12:40:00.000
 1     3    2016-04-04 10:33:00.000 1   2016-04-04 11:38:00.000

答案 2 :(得分:0)

SQL Server 2012+支持累积总和。您可以通过累计计算* 0 * s的数量来获取活动块。活动块将具有恒定值。然后,您可以聚合(和过滤)以获取活动时段:

select deviceid, min(timestamp), max(timestamp)
from (select t.*,
             sum(case when value = 0 then 1 else 0 end) over
                 (partition by deviceid order by timestamp) as grp
      from t
     ) t
where value <> 0
group by deviceid, grp;

在早期版本的SQL Server中,您可以使用outer apply(以及其他方法)执行类似的操作。