总结数据孤岛
Summarising Data Islands
我一直在阅读大量有关数据孤岛的资料,并使用 CTE 或大量子查询进行总结。大多数似乎都依赖于日期的巧妙数学,这看起来很酷,但我认为它对我不起作用。
我们有许多车辆数据记录器按不同的时间表发送状态更新。我正在寻找一种更快、基于非循环的方法来总结某些状态。
- NodeId 设备标识符
- LogId 日志条目 PK
- AssembledTime记录在设备上组装的时间
- ReceivedTime服务器收到记录的时间
- 速度 记录时的速度
- StatusText 可以包含多个关键字
数据通常在行程结束时处理。(点火到点火关闭)有一个提供 NodeId、StartTime 和 EndTime 的 LogTrips。目前,我循环遍历按 AssembledTime 排序的日志条目,寻找类似 %whatever% 的 StatusText。根据客户的首选状态,我会这样做几次。例如:(StatusText like '%seatbelt%' 或 StatusText like '%s/b%')和 lm.Speed > 10,对于没有系安全带的人
经过一番阅读后,我发现我可以使用 row_number() 创建适当的顺序记录,并使用 case when...end
提取所需的状态
SELECT RowNumber = ROW_NUMBER() OVER(ORDER BY l.AssembledTime),
l.NodeId,
l.LogId,
l.AssembledTime,
lm.Speed,
lm.StatusText,
StatusSpeed = CASE WHEN lm.StatusText like '%speed%' THEN 1 ELSE 0 END,
StatusAccident = CASE WHEN lm.StatusText like '%accident%' THEN 1 ELSE 0 END, --impact?
StatusSeatbeltDriving = CASE WHEN (lm.StatusText like '%seatbelt%' or lm.StatusText like '%s/b%') and lm.Speed > 10 THEN 1 ELSE 0 END,
StatusSeatbeltIdle = CASE WHEN (lm.StatusText like '%seatbelt%' or lm.StatusText like '%s/b%') and lm.Speed = 0 THEN 1 ELSE 0 END,
Status4wd = CASE WHEN (lm.StatusText like '%4wd%' or lm.StatusText like '%4x4%') THEN 1 ELSE 0 END
FROM Ctrack6.dbo.Logs l
JOIN Ctrack6.dbo.LogMobiles lm on l.LogId = lm.LogId
WHERE l.NodeId = @NodeId
AND l.AssembledTime between @TripStart AND @TripEnd
这会给我一个设备行程的所有日志的列表,顺序为:
RowNumber NodeId LogId AssembledTime Speed StatusText StatusSpeed StatusAccident StatusSeatbeltDriving StatusSeatbeltIdle Status4wd IsProcessed
1 3099 308815155 2015-05-26 11:05:43.000 0 Start up 0 0 0 0 0 0
2 3099 308815156 2015-05-26 11:05:55.000 0 Driving 0 0 0 0 0 0
3 3099 308815157 2015-05-26 11:06:25.000 10 Driving 0 0 0 0 0 0
4 3099 308815158 2015-05-26 11:06:45.000 11 Driving 0 0 0 0 0 0
5 3099 308815344 2015-05-26 11:07:15.000 0 Driving 0 0 0 0 0 0
6 3099 308815345 2015-05-26 11:07:16.000 0 Seatbelt 0 0 0 1 0 0
7 3099 308815477 2015-05-26 11:07:19.000 0 Seatbelt 0 0 0 1 0 0
8 3099 308815479 2015-05-26 11:07:24.000 0 Seatbelt 0 0 0 1 0 0
9 3099 308815481 2015-05-26 11:07:29.000 0 Seatbelt 0 0 0 1 0 0
10 3099 308815482 2015-05-26 11:07:34.000 0 Seatbelt 0 0 0 1 0 0
11 3099 308815598 2015-05-26 11:07:39.000 0 Seatbelt 0 0 0 1 0 0
12 3099 308815599 2015-05-26 11:07:44.000 0 Seatbelt 0 0 0 1 0 0
13 3099 308815600 2015-05-26 11:07:49.000 0 Seatbelt 0 0 0 1 0 0
14 3099 308815601 2015-05-26 11:07:54.000 0 Seatbelt 0 0 0 1 0 0
15 3099 308815729 2015-05-26 11:08:00.000 0 Seatbelt 0 0 0 1 0 0
16 3099 308815730 2015-05-26 11:08:05.000 0 Seatbelt 0 0 0 1 0 0
17 3099 308815731 2015-05-26 11:08:10.000 0 Seatbelt 0 0 0 1 0 0
18 3099 308815732 2015-05-26 11:08:15.000 0 Seatbelt 0 0 0 1 0 0
19 3099 308816439 2015-05-26 11:08:45.000 0 Seatbelt 0 0 0 1 0 0
20 3099 308816440 2015-05-26 11:09:15.000 0 Seatbelt 0 0 0 1 0 0
21 3099 308816441 2015-05-26 11:09:45.000 0 Seatbelt 0 0 0 1 0 0
22 3099 308816442 2015-05-26 11:10:07.000 0 Ignition off 0 0 0 0 0 0
所需的结果将汇总第 6-21 行。有
- NodeId - 设备的NodeId
- StartLogId 第 6 行的 LogId
- EndLogId 第 21 行的 LogId
- EventStartTime 第 6 行的 AssembledTime
- EventEndTime第21行的集合时间
- 事件类型 'Seatbelt'
如果有多个岛,就会有多个摘要
我只是没有得到我可以用来制作我的岛屿的东西。
What do I group by?
我这样做的方法是用 1 标记每个 'state change'。然后我在那个字段上做一个 运行ning 总计。这为每个 'group' 提供了一个递增的唯一编号,您可以将其分组。
就我个人而言,我总是将这些东西加载到暂存中 table 进行处理,而不是尝试使用一堆内联子选择,原因有两个:
- 由于各种原因,它通常更快(您可以创建特定索引,可以优化数据大小)
- 您可以添加许多额外的 'helper' 列,这些列允许您调试这个众所周知的复杂处理(即将您的自由文本评论映射到已知状态)
此外,在分期中 table 您可以使用这样的结构来 'gobble up' 岛屿:
SELECT 1
WHILE @@ROWCOUNT<> 0
BEGIN
UPDATE TGT
SET ChangeColumn=1
FROM YourTable TGT
INNER JOIN YourTable PriorRow
WHERE PriorRow.RowNum-1 = TGT.RowNum
AND PriorRow.State = TGT.State
AND ChangeColumn=0
END
如果你的 运行 这个它保持 运行ning 直到找到并标记所有状态变化
我在以下页面找到了我的解决方案:https://www.simple-talk.com/sql/t-sql-programming/the-sql-of-gaps-and-islands-in-sequences/
更具体地说:
添加了两个 Table 个变量
declare @logs table
(
LogId int PRIMARY KEY,
RowNumberAll int,
RowNumberNode int,
NodeId int,
AssembledTime datetime,
Speed int,
StatusText varchar(200),
StatusSpeed bit,
StatusAccident bit,
StatusSeatbeltDriving bit,
StatusSeatbeltIdle bit,
Status4wd bit,
UNIQUE(Nodeid, RowNumberNode),
UNIQUE(RowNumberAll)
)
declare @results table
(
EventType varchar(50),
NodeId int,
StartSeqNo int,
EndSeqNo int,
LogCount int,
UNIQUE(NodeId, StartSeqNo, EventType)
)
添加了一个额外的列 RowNumberNode 来查询。
RowNumberNode = ROW_NUMBER() OVER(PARTITION BY NodeId ORDER BY l.AssembledTime),
稍微修改了示例以使用我的代码。对于我想要晒黑的每个状态,我都有这些块中的 1 个
INSERT INTO @results (EventType, NodeId, StartSeqNo, EndSeqNo, LogCount)
SELECT 'Speed',
NodeId,
StartSeqNo=MIN(RowNumberNode),
EndSeqNo=MAX(RowNumberNode),
LogCount=MAX(RowNumberNode) - MIN(RowNumberNode) + 1
FROM
(
SELECT NodeId,
RowNumberNode,
rn=RowNumberNode-ROW_NUMBER() OVER (PARTITION BY NodeId ORDER BY RowNumberNode)
FROM @logs
WHERE StatusSpeed=1
) a
GROUP BY NodeId, rn
--HAVING MIN(RowNumberNode) - MAX(RowNumberNode) > 0
ORDER BY NodeId, StartSeqNo;
我一直在阅读大量有关数据孤岛的资料,并使用 CTE 或大量子查询进行总结。大多数似乎都依赖于日期的巧妙数学,这看起来很酷,但我认为它对我不起作用。
我们有许多车辆数据记录器按不同的时间表发送状态更新。我正在寻找一种更快、基于非循环的方法来总结某些状态。
- NodeId 设备标识符
- LogId 日志条目 PK
- AssembledTime记录在设备上组装的时间
- ReceivedTime服务器收到记录的时间
- 速度 记录时的速度
- StatusText 可以包含多个关键字
数据通常在行程结束时处理。(点火到点火关闭)有一个提供 NodeId、StartTime 和 EndTime 的 LogTrips。目前,我循环遍历按 AssembledTime 排序的日志条目,寻找类似 %whatever% 的 StatusText。根据客户的首选状态,我会这样做几次。例如:(StatusText like '%seatbelt%' 或 StatusText like '%s/b%')和 lm.Speed > 10,对于没有系安全带的人
经过一番阅读后,我发现我可以使用 row_number() 创建适当的顺序记录,并使用 case when...end
提取所需的状态SELECT RowNumber = ROW_NUMBER() OVER(ORDER BY l.AssembledTime),
l.NodeId,
l.LogId,
l.AssembledTime,
lm.Speed,
lm.StatusText,
StatusSpeed = CASE WHEN lm.StatusText like '%speed%' THEN 1 ELSE 0 END,
StatusAccident = CASE WHEN lm.StatusText like '%accident%' THEN 1 ELSE 0 END, --impact?
StatusSeatbeltDriving = CASE WHEN (lm.StatusText like '%seatbelt%' or lm.StatusText like '%s/b%') and lm.Speed > 10 THEN 1 ELSE 0 END,
StatusSeatbeltIdle = CASE WHEN (lm.StatusText like '%seatbelt%' or lm.StatusText like '%s/b%') and lm.Speed = 0 THEN 1 ELSE 0 END,
Status4wd = CASE WHEN (lm.StatusText like '%4wd%' or lm.StatusText like '%4x4%') THEN 1 ELSE 0 END
FROM Ctrack6.dbo.Logs l
JOIN Ctrack6.dbo.LogMobiles lm on l.LogId = lm.LogId
WHERE l.NodeId = @NodeId
AND l.AssembledTime between @TripStart AND @TripEnd
这会给我一个设备行程的所有日志的列表,顺序为:
RowNumber NodeId LogId AssembledTime Speed StatusText StatusSpeed StatusAccident StatusSeatbeltDriving StatusSeatbeltIdle Status4wd IsProcessed
1 3099 308815155 2015-05-26 11:05:43.000 0 Start up 0 0 0 0 0 0
2 3099 308815156 2015-05-26 11:05:55.000 0 Driving 0 0 0 0 0 0
3 3099 308815157 2015-05-26 11:06:25.000 10 Driving 0 0 0 0 0 0
4 3099 308815158 2015-05-26 11:06:45.000 11 Driving 0 0 0 0 0 0
5 3099 308815344 2015-05-26 11:07:15.000 0 Driving 0 0 0 0 0 0
6 3099 308815345 2015-05-26 11:07:16.000 0 Seatbelt 0 0 0 1 0 0
7 3099 308815477 2015-05-26 11:07:19.000 0 Seatbelt 0 0 0 1 0 0
8 3099 308815479 2015-05-26 11:07:24.000 0 Seatbelt 0 0 0 1 0 0
9 3099 308815481 2015-05-26 11:07:29.000 0 Seatbelt 0 0 0 1 0 0
10 3099 308815482 2015-05-26 11:07:34.000 0 Seatbelt 0 0 0 1 0 0
11 3099 308815598 2015-05-26 11:07:39.000 0 Seatbelt 0 0 0 1 0 0
12 3099 308815599 2015-05-26 11:07:44.000 0 Seatbelt 0 0 0 1 0 0
13 3099 308815600 2015-05-26 11:07:49.000 0 Seatbelt 0 0 0 1 0 0
14 3099 308815601 2015-05-26 11:07:54.000 0 Seatbelt 0 0 0 1 0 0
15 3099 308815729 2015-05-26 11:08:00.000 0 Seatbelt 0 0 0 1 0 0
16 3099 308815730 2015-05-26 11:08:05.000 0 Seatbelt 0 0 0 1 0 0
17 3099 308815731 2015-05-26 11:08:10.000 0 Seatbelt 0 0 0 1 0 0
18 3099 308815732 2015-05-26 11:08:15.000 0 Seatbelt 0 0 0 1 0 0
19 3099 308816439 2015-05-26 11:08:45.000 0 Seatbelt 0 0 0 1 0 0
20 3099 308816440 2015-05-26 11:09:15.000 0 Seatbelt 0 0 0 1 0 0
21 3099 308816441 2015-05-26 11:09:45.000 0 Seatbelt 0 0 0 1 0 0
22 3099 308816442 2015-05-26 11:10:07.000 0 Ignition off 0 0 0 0 0 0
所需的结果将汇总第 6-21 行。有
- NodeId - 设备的NodeId
- StartLogId 第 6 行的 LogId
- EndLogId 第 21 行的 LogId
- EventStartTime 第 6 行的 AssembledTime
- EventEndTime第21行的集合时间
- 事件类型 'Seatbelt'
如果有多个岛,就会有多个摘要
我只是没有得到我可以用来制作我的岛屿的东西。
What do I group by?
我这样做的方法是用 1 标记每个 'state change'。然后我在那个字段上做一个 运行ning 总计。这为每个 'group' 提供了一个递增的唯一编号,您可以将其分组。
就我个人而言,我总是将这些东西加载到暂存中 table 进行处理,而不是尝试使用一堆内联子选择,原因有两个:
- 由于各种原因,它通常更快(您可以创建特定索引,可以优化数据大小)
- 您可以添加许多额外的 'helper' 列,这些列允许您调试这个众所周知的复杂处理(即将您的自由文本评论映射到已知状态)
此外,在分期中 table 您可以使用这样的结构来 'gobble up' 岛屿:
SELECT 1
WHILE @@ROWCOUNT<> 0
BEGIN
UPDATE TGT
SET ChangeColumn=1
FROM YourTable TGT
INNER JOIN YourTable PriorRow
WHERE PriorRow.RowNum-1 = TGT.RowNum
AND PriorRow.State = TGT.State
AND ChangeColumn=0
END
如果你的 运行 这个它保持 运行ning 直到找到并标记所有状态变化
我在以下页面找到了我的解决方案:https://www.simple-talk.com/sql/t-sql-programming/the-sql-of-gaps-and-islands-in-sequences/
更具体地说:
添加了两个 Table 个变量
declare @logs table
(
LogId int PRIMARY KEY,
RowNumberAll int,
RowNumberNode int,
NodeId int,
AssembledTime datetime,
Speed int,
StatusText varchar(200),
StatusSpeed bit,
StatusAccident bit,
StatusSeatbeltDriving bit,
StatusSeatbeltIdle bit,
Status4wd bit,
UNIQUE(Nodeid, RowNumberNode),
UNIQUE(RowNumberAll)
)
declare @results table
(
EventType varchar(50),
NodeId int,
StartSeqNo int,
EndSeqNo int,
LogCount int,
UNIQUE(NodeId, StartSeqNo, EventType)
)
添加了一个额外的列 RowNumberNode 来查询。
RowNumberNode = ROW_NUMBER() OVER(PARTITION BY NodeId ORDER BY l.AssembledTime),
稍微修改了示例以使用我的代码。对于我想要晒黑的每个状态,我都有这些块中的 1 个
INSERT INTO @results (EventType, NodeId, StartSeqNo, EndSeqNo, LogCount)
SELECT 'Speed',
NodeId,
StartSeqNo=MIN(RowNumberNode),
EndSeqNo=MAX(RowNumberNode),
LogCount=MAX(RowNumberNode) - MIN(RowNumberNode) + 1
FROM
(
SELECT NodeId,
RowNumberNode,
rn=RowNumberNode-ROW_NUMBER() OVER (PARTITION BY NodeId ORDER BY RowNumberNode)
FROM @logs
WHERE StatusSpeed=1
) a
GROUP BY NodeId, rn
--HAVING MIN(RowNumberNode) - MAX(RowNumberNode) > 0
ORDER BY NodeId, StartSeqNo;