根据特定 start/end 值过滤数据并计算时间
Filter data based on specific start/end values and calculate time
我在过滤掉特定数据集以捕获子集并计算该子集的持续时间时遇到问题。
我们有 PC 在某些任务开启时进行记录;开机时间、BIOS 加载时间、windows 启动时间、应用程序启动时间等——以及这些任务关闭的时间。我的目标集中在 windows 开启的时间和 on/off 关闭 windows 之前的任务以及 windows 开启 on/off 的持续时间。
原始数据集:
PCNum Code On/Off Date Time
1. 5258 power on 10/11/2019 10:00:00
2. 5258 bios on 10/11/2019 10:00:10
3. 5258 windows on 10/11/2019 10:00:20
4. 5258 steam on 10/11/2019 10:00:55
5. 5258 origin on 10/11/2019 10:01:03
6. 5258 origin off 10/11/2019 10:10:04
7. 5258 steam off 10/11/2019 10:12:45
8. 5258 windows off 10/11/2019 10:13:06
9. 5258 bios off 10/11/2019 10:14:01
10. 5258 power off 10/11/2019 10:14:22
11. 5258 power on 10/11/2019 11:34:45
12. 5258 bios on 10/11/2019 11:34:56
13. 5258 windows on 10/11/2019 11:35:03
14. 5258 skype on 10/11/2019 11:35:06
15. 5258 skype off 10/11/2019 11:56:52
16. 5258 windows off 10/11/2019 11:57:07
17. 5258 bios off 10/11/2019 11:57:36
18. 5258 power off 10/11/2019 11:57:48
使用 CTE,我已经能够过滤我的数据,因此它在 windows 开启时进行第一次迭代,在关闭时进行最后一次迭代,如下所示:
with minTime_cte (PCNum, Code, Date, Time)
as
-- find first occurance of on for windows code
(select PCNum,
Code,
Date,
Time = min(Time)
from PCData
where Code = 'windows' and [On/Off] = 'on'
group by PCNum, Code),
maxTime_cte (PCNum, Code, Date, Time)
as
-- find last occurrence of off for windows code
(select PCNum,
Code,
Date,
Time = max(Time)
from PCData
where Code = 'windows' and [On/Off] = 'off'
group by PCNum, Code),
select PCNum,
Code,
[On/Off],
Date,
Time
from PCData
join minTime_cte on minTime_cte.Date = Date
and minTime_cte.PCNum = PCNum
join maxTime_cte on maxTime_cte.Date = Date
and maxTime_cte.PCNum = PCNum
where Time >= minTime_cte.Time
and Time <= maxTime_cte.Time
order by PCNum, Date, Time
PCNum Code On/Off Date Time
1. 5258 windows on 10/11/2019 10:00:20
2. 5258 steam on 10/11/2019 10:00:55
3. 5258 origin on 10/11/2019 10:01:03
4. 5258 origin off 10/11/2019 10:10:04
5. 5258 steam off 10/11/2019 10:12:45
6. 5258 windows off 10/11/2019 10:13:06
7. 5258 bios off 10/11/2019 10:14:01
8. 5258 power off 10/11/2019 10:14:22
9. 5258 power on 10/11/2019 11:34:45
10. 5258 bios on 10/11/2019 11:34:56
11. 5258 windows on 10/11/2019 11:35:03
12. 5258 skype on 10/11/2019 11:35:06
13. 5258 skype off 10/11/2019 11:56:52
14. 5258 windows off 10/11/2019 11:57:07
现在我需要进一步过滤集合,以排除 windows 关闭和再次打开之间的任何任务。在这种情况下,它将删除记录 7、8、9 和 10。我还需要计算从 windows 打开到关闭的持续时间。
这是我想要的结果:
PCNum Code On/Off Date Time DurationOfWindowsSec
1. 5258 windows on 10/11/2019 10:00:20 0:12:46
2. 5258 steam on 10/11/2019 10:00:55 0:12:46
3. 5258 origin on 10/11/2019 10:01:03 0:12:46
4. 5258 origin off 10/11/2019 10:10:04 0:12:46
5. 5258 steam off 10/11/2019 10:12:45 0:12:46
6. 5258 windows off 10/11/2019 10:13:06 0:12:46
7. 5258 windows on 10/11/2019 11:35:03 0:22:04
8. 5258 skype on 10/11/2019 11:35:06 0:22:04
9. 5258 skype off 10/11/2019 11:56:52 0:22:04
10. 5258 windows off 10/11/2019 11:57:07 0:22:04
任何 help/direction 将不胜感激。谢谢!
我解决这个问题的方法是弄清楚最后一个 windows
事件是什么,然后根据它进行过滤。如果最后一个事件是on
,那么我们应该记录它;否则我们不会。作为奖励,这意味着您可能不需要之前的预处理——我还没有测试所有的边缘情况,但是如果您的数据以 windows
off 开头,那么您应该很好(这样您就可以合并有一个虚拟行)
请注意,为方便起见,我对数据类型做了一些小改动——我将日期和时间列转换为单个日期时间列,并将 OnOff 转换为位列。回过头来看,也许第二次调整是没有必要的。进行第一次调整应该很容易;您可以将其作为预处理 CTE 的一部分。
我已经把你的数据放到了一个临时文件中 table,所以我可以更容易地进行实验;您可能希望将 #test
的每个实例替换为您的 CTE 的名称。
SQLFiddle/数据定义:
create table #test(
id int primary key identity,
PCNum int,
Code varchar(50),
OnOff bit,
dt datetime
)
;
insert into #test(PCNum, Code, OnOff, dt)
values
(5258, 'windows' , 1, '10/11/2019 10:00:20'),
(5258, 'steam' , 1, '10/11/2019 10:00:55'),
(5258, 'origin' , 1, '10/11/2019 10:01:03'),
(5258, 'origin' , 0, '10/11/2019 10:10:04'),
(5258, 'steam' , 0, '10/11/2019 10:12:45'),
(5258, 'windows' , 0, '10/11/2019 10:13:06'),
(5258, 'bios' , 0, '10/11/2019 10:14:01'),
(5258, 'power' , 0, '10/11/2019 10:14:22'),
(5258, 'power' , 1, '10/11/2019 11:34:45'),
(5258, 'bios' , 1, '10/11/2019 11:34:56'),
(5258, 'windows' , 1, '10/11/2019 11:35:03'),
(5258, 'skype' , 1, '10/11/2019 11:35:06'),
(5258, 'skype' , 0, '10/11/2019 11:56:52'),
(5258, 'windows' , 0, '10/11/2019 11:57:07')
;
实际查询:
with last_windows(id, dt) as
(
select t1.id, max(t2.dt) as dt from #test as t1
cross join #test as t2
where datediff(second, t1.dt, t2.dt) <= 0
and t2.code = 'windows'
group by t1.id
),
windows_active(id, OnOff) as
(
select w.id, t.OnOff
from #test as t
left join last_windows as w on w.dt = t.dt
where t.Code = 'windows'
)
select t.id, t.PCNum, t.Code, t.OnOff, t.dt
from #test as t
left join windows_active as wa on wa.id = t.id
where wa.OnOff = 1
or t.Code = 'windows'
;
说明:CTE last_windows
负责识别每一行(ID)最后windows
activity 的时间。它通过交叉连接 table 来实现这一点。第一个实例命名为 t1
,第二个实例命名为 t2
。
t2
被过滤为仅包含 windows
行,并且还仅包含 t1
或之前的事件。如果我们然后为 t1
中的每个 id 取最大 t2
日期时间,这表示最后 windows
activity.
的时间
由于聚合的工作方式,我们实际上无法将 OnOff 数据添加到 last_windows
。 windows_active
提供了一个便利 - 它将 #test
连接到 last_windows
,有效地将 OnOff 数据添加到 last_windows
。我们剩下的是一个 table,它告诉我们最后一个 windows
activity 是什么,对于 #test
.
中的每一行
查询的其余部分很简单 - 查询将 windows_active
连接到 #test
并仅在最后一个 windows
事件为 'on' 时进行过滤。这也会过滤掉任何 windows 被关闭的情况,因此有一个 or
语句来包含所有 windows
事件。
请注意,不幸的是,由于交叉连接,此查询在大型数据集上可能会很慢。
我在过滤掉特定数据集以捕获子集并计算该子集的持续时间时遇到问题。
我们有 PC 在某些任务开启时进行记录;开机时间、BIOS 加载时间、windows 启动时间、应用程序启动时间等——以及这些任务关闭的时间。我的目标集中在 windows 开启的时间和 on/off 关闭 windows 之前的任务以及 windows 开启 on/off 的持续时间。
原始数据集:
PCNum Code On/Off Date Time
1. 5258 power on 10/11/2019 10:00:00
2. 5258 bios on 10/11/2019 10:00:10
3. 5258 windows on 10/11/2019 10:00:20
4. 5258 steam on 10/11/2019 10:00:55
5. 5258 origin on 10/11/2019 10:01:03
6. 5258 origin off 10/11/2019 10:10:04
7. 5258 steam off 10/11/2019 10:12:45
8. 5258 windows off 10/11/2019 10:13:06
9. 5258 bios off 10/11/2019 10:14:01
10. 5258 power off 10/11/2019 10:14:22
11. 5258 power on 10/11/2019 11:34:45
12. 5258 bios on 10/11/2019 11:34:56
13. 5258 windows on 10/11/2019 11:35:03
14. 5258 skype on 10/11/2019 11:35:06
15. 5258 skype off 10/11/2019 11:56:52
16. 5258 windows off 10/11/2019 11:57:07
17. 5258 bios off 10/11/2019 11:57:36
18. 5258 power off 10/11/2019 11:57:48
使用 CTE,我已经能够过滤我的数据,因此它在 windows 开启时进行第一次迭代,在关闭时进行最后一次迭代,如下所示:
with minTime_cte (PCNum, Code, Date, Time)
as
-- find first occurance of on for windows code
(select PCNum,
Code,
Date,
Time = min(Time)
from PCData
where Code = 'windows' and [On/Off] = 'on'
group by PCNum, Code),
maxTime_cte (PCNum, Code, Date, Time)
as
-- find last occurrence of off for windows code
(select PCNum,
Code,
Date,
Time = max(Time)
from PCData
where Code = 'windows' and [On/Off] = 'off'
group by PCNum, Code),
select PCNum,
Code,
[On/Off],
Date,
Time
from PCData
join minTime_cte on minTime_cte.Date = Date
and minTime_cte.PCNum = PCNum
join maxTime_cte on maxTime_cte.Date = Date
and maxTime_cte.PCNum = PCNum
where Time >= minTime_cte.Time
and Time <= maxTime_cte.Time
order by PCNum, Date, Time
PCNum Code On/Off Date Time
1. 5258 windows on 10/11/2019 10:00:20
2. 5258 steam on 10/11/2019 10:00:55
3. 5258 origin on 10/11/2019 10:01:03
4. 5258 origin off 10/11/2019 10:10:04
5. 5258 steam off 10/11/2019 10:12:45
6. 5258 windows off 10/11/2019 10:13:06
7. 5258 bios off 10/11/2019 10:14:01
8. 5258 power off 10/11/2019 10:14:22
9. 5258 power on 10/11/2019 11:34:45
10. 5258 bios on 10/11/2019 11:34:56
11. 5258 windows on 10/11/2019 11:35:03
12. 5258 skype on 10/11/2019 11:35:06
13. 5258 skype off 10/11/2019 11:56:52
14. 5258 windows off 10/11/2019 11:57:07
现在我需要进一步过滤集合,以排除 windows 关闭和再次打开之间的任何任务。在这种情况下,它将删除记录 7、8、9 和 10。我还需要计算从 windows 打开到关闭的持续时间。
这是我想要的结果:
PCNum Code On/Off Date Time DurationOfWindowsSec
1. 5258 windows on 10/11/2019 10:00:20 0:12:46
2. 5258 steam on 10/11/2019 10:00:55 0:12:46
3. 5258 origin on 10/11/2019 10:01:03 0:12:46
4. 5258 origin off 10/11/2019 10:10:04 0:12:46
5. 5258 steam off 10/11/2019 10:12:45 0:12:46
6. 5258 windows off 10/11/2019 10:13:06 0:12:46
7. 5258 windows on 10/11/2019 11:35:03 0:22:04
8. 5258 skype on 10/11/2019 11:35:06 0:22:04
9. 5258 skype off 10/11/2019 11:56:52 0:22:04
10. 5258 windows off 10/11/2019 11:57:07 0:22:04
任何 help/direction 将不胜感激。谢谢!
我解决这个问题的方法是弄清楚最后一个 windows
事件是什么,然后根据它进行过滤。如果最后一个事件是on
,那么我们应该记录它;否则我们不会。作为奖励,这意味着您可能不需要之前的预处理——我还没有测试所有的边缘情况,但是如果您的数据以 windows
off 开头,那么您应该很好(这样您就可以合并有一个虚拟行)
请注意,为方便起见,我对数据类型做了一些小改动——我将日期和时间列转换为单个日期时间列,并将 OnOff 转换为位列。回过头来看,也许第二次调整是没有必要的。进行第一次调整应该很容易;您可以将其作为预处理 CTE 的一部分。
我已经把你的数据放到了一个临时文件中 table,所以我可以更容易地进行实验;您可能希望将 #test
的每个实例替换为您的 CTE 的名称。
SQLFiddle/数据定义:
create table #test(
id int primary key identity,
PCNum int,
Code varchar(50),
OnOff bit,
dt datetime
)
;
insert into #test(PCNum, Code, OnOff, dt)
values
(5258, 'windows' , 1, '10/11/2019 10:00:20'),
(5258, 'steam' , 1, '10/11/2019 10:00:55'),
(5258, 'origin' , 1, '10/11/2019 10:01:03'),
(5258, 'origin' , 0, '10/11/2019 10:10:04'),
(5258, 'steam' , 0, '10/11/2019 10:12:45'),
(5258, 'windows' , 0, '10/11/2019 10:13:06'),
(5258, 'bios' , 0, '10/11/2019 10:14:01'),
(5258, 'power' , 0, '10/11/2019 10:14:22'),
(5258, 'power' , 1, '10/11/2019 11:34:45'),
(5258, 'bios' , 1, '10/11/2019 11:34:56'),
(5258, 'windows' , 1, '10/11/2019 11:35:03'),
(5258, 'skype' , 1, '10/11/2019 11:35:06'),
(5258, 'skype' , 0, '10/11/2019 11:56:52'),
(5258, 'windows' , 0, '10/11/2019 11:57:07')
;
实际查询:
with last_windows(id, dt) as
(
select t1.id, max(t2.dt) as dt from #test as t1
cross join #test as t2
where datediff(second, t1.dt, t2.dt) <= 0
and t2.code = 'windows'
group by t1.id
),
windows_active(id, OnOff) as
(
select w.id, t.OnOff
from #test as t
left join last_windows as w on w.dt = t.dt
where t.Code = 'windows'
)
select t.id, t.PCNum, t.Code, t.OnOff, t.dt
from #test as t
left join windows_active as wa on wa.id = t.id
where wa.OnOff = 1
or t.Code = 'windows'
;
说明:CTE last_windows
负责识别每一行(ID)最后windows
activity 的时间。它通过交叉连接 table 来实现这一点。第一个实例命名为 t1
,第二个实例命名为 t2
。
t2
被过滤为仅包含 windows
行,并且还仅包含 t1
或之前的事件。如果我们然后为 t1
中的每个 id 取最大 t2
日期时间,这表示最后 windows
activity.
由于聚合的工作方式,我们实际上无法将 OnOff 数据添加到 last_windows
。 windows_active
提供了一个便利 - 它将 #test
连接到 last_windows
,有效地将 OnOff 数据添加到 last_windows
。我们剩下的是一个 table,它告诉我们最后一个 windows
activity 是什么,对于 #test
.
查询的其余部分很简单 - 查询将 windows_active
连接到 #test
并仅在最后一个 windows
事件为 'on' 时进行过滤。这也会过滤掉任何 windows 被关闭的情况,因此有一个 or
语句来包含所有 windows
事件。
请注意,不幸的是,由于交叉连接,此查询在大型数据集上可能会很慢。