我有一个(SQL Server)数据库表,其中包含设备的活动日志。该表由DeviceId,时间戳(DateTime)和值字段组成。无论何时打开或关闭(值为1或0),设备都会将其状态更改写入数据库。
现在我想知道什么是获得"活动块的最快方法"从那张桌子。那是什么意思?我希望获得由" 1"定义的所有时间段。价值及其随后的" 0"给定DeviceId的值,以便得到这样的时间范围列表(对于活动块,非活动时间将介于0值后跟1之间):
DateTime ActiveStart, DateTime ActiveEnd
我目前最终首先获得EF作为列表的所有条目,然后循环遍历它们并将每个条目与其前任进行比较,以检查设备是否已打开和关闭。
确实工作,但我确实认为必须有一种更好,更高效的方法。最好的方法是什么?可以使用纯SQL查询(我可以从中构建存储过程)或LINQ to SQL查询。
感谢您的想法和意见!
答案 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
(以及其他方法)执行类似的操作。