根据时间、ID 和事件类型从未排序的 table 事件中删除某些事件
Removing certain events from an unsorted table of events based on time, id and type of event
TL;DR:我正在尝试从传入的事件列表中删除特定类型事件的重复项。我有 5-20 百万条记录。应删除缩进行:
sn# time mo method
02848 1504725241 R P
02848 1504725365 R F1.0
02848 1504725366 R F1.6
02848 1504725366 R F2.0
02848 1504727651 R P
02848 1504727681 R P
02848 1504727741 R F1.0
02848 1504728165 R E
详细说明:
为了提供一些上下文,这些是执行的用户操作。其中模式是用户正在使用的服务类型。而方法就是用户所做的。比如P是pause的缩写,F是fast forward的缩写
我正在研究的 table 具有以下结构:
CREATE TABLE events
([serialnumber] int, [time] int, [mode] varchar(1), [method] varchar(4))
它每小时更新一次,包含 5 到 2000 万条记录。
示例数据如上所示。
我希望创建会话以填写以下内容table:
CREATE TABLE sessions(serialnumber int, mode varchar(1), method varchar(1), startTime int, endTime int);
我想要实现的是基于这些事件创建会话,并使用一个重要的过滤器。我不想使用方法 F 重复事件,我只想要第一个 F 事件,连接到任何非 F 接下来是活动。也就是说,对于以上我想在处理后有以下会话,即我想要的结果是:
#sn metho mod starttime endtime
02848 R P 1504725241 1504725365
02848 R F 1504725365 1504727651
02848 R P 1504727651 1504727681
02848 R P 1504727681 1504727741
02848 R F 1504727741 1504728165
这里F1.0、F1.6、F2.0等都改成了F,保留一组F事件的第一个时间戳,去掉了重复。由于大量记录,我无法有效地执行此操作。
截至目前,我正在尝试以下操作:
- 当 table 更新时,删除所有 F 事件,除了每组 F 事件中的第一个事件, pr序列号。
- 使用
cross apply
很像 LAG
函数来生成会话列表。
完整的 SQL 代码如下所示:
--Delete recurring F events
DELETE outside
FROM events AS outside
CROSS APPLY
(
SELECT TOP 1 inside.[Time]
FROM events inside
WHERE outside.serialnumber = inside.serialnumber
AND (outside.[Time] > inside.[Time]
OR (outside.[Time] = inside.[Time]
AND outside.[method] != inside.[method]
)
)
AND LEFT(outside.[method],1) = 'F'
AND LEFT(inside.[method],1) = 'F'
AND NOT EXISTS
(SELECT innerInside.[Time] FROM events innerInside
WHERE innerInside.serialnumber = inside.serialnumber
AND innerInside.[Time] <= outside.[Time]
AND innerInside.[Time] > inside.[Time]
AND LEFT(innerInside.[method],1) != 'F'
)
ORDER BY inside.[Time] DESC
) processed_inside
--Create sessions from events
SELECT serialnumber
,outside.[mode]
,LEFT(outside.[method],1) AS 'Method'
,outside.[Time] AS 'SessionStart'
,processed_inside.[Time] AS 'SessionEnd'
FROM events AS outside
CROSS APPLY
(
SELECT MIN( inside.[Time]) AS 'Time'
FROM events AS inside
WHERE outside.serialnumber = inside.serialnumber
AND outside.[Time] < inside.[Time]
) processed_inside
WHERE processed_inside.[Time] IS NOT NULL
这可行,但删除步骤慢得无法忍受。这是删除步骤的实际执行计划的图片。
(请注意,这是实际的数据库,因此它看起来与 post 中的简化示例略有不同。)
我也考虑过将第 1 步作为第 2 步的一部分进行,但尚未找到任何有意义的方法。最后,我正在寻找一种方法来有效地创建会话而不生成太多会话(因此删除类型 F 的事件)。
如果您一路走到这里,可以在这里找到 sqlfiddle:http://sqlfiddle.com/#!6/a5392/1。我在其中添加了上述两个功能。输出 table 是期望的结果。
如果您能给我任何帮助,我将不胜感激。
第二次更新2017100915:35
这应该可以处理您所有的示例数据。
关于旧版本附加组件的一些解释:
Q1查询中的last条件(OR(RN=1 AND method='F')是最后离开
F(因为后者对于计算聚合 F 的结束时间很有用);
在 Q11 中我使用了两个 ROW_NUMBER() 来查找连续的方法和
WHERE条件中排除除
最后一个(如果存在)
- 在 WITH 内的最后一个查询 (Q2) 中,结束时间是使用 (SELECT TOP
1 ....)
- 最后一个 WHERE 条件是删除(如果存在)结尾(但
已经累计)F法(见上文第1点)
- 也看我之前版本说的
WITH Q1 AS (SELECT *
FROM (SELECT serialnumber, [time] AS TTIME, mode, LEFT(method,1) AS method, SUBSTRING(method,2,3) AS rest_of_method
, ROW_NUMBER() OVER (PARTITION BY serialnumber ORDER BY [time] DESC) AS RN
FROM events
)A
WHERE method<>'F' OR rest_of_method='1.0' OR (RN=1 AND method='F')
/* AND serialnumber=6363*/ /* test*/
)
, Q11 AS (SELECT Q1a.serialnumber, Q1a.mode, Q1a.method, Q1a.starttime, RN
FROM (SELECT serialnumber, mode, method, TTIME AS starttime
, ROW_NUMBER() OVER (PARTITION BY serialnumber ORDER BY TTIME, method) AS consec_id1
, RN
FROM Q1) Q1a
LEFT JOIN (SELECT serialnumber, mode, method, TTIME AS starttime
, ROW_NUMBER() OVER (PARTITION BY serialnumber ORDER BY TTIME, method) AS consec_id2
FROM Q1) Q1b on Q1b.serialnumber=Q1a.serialnumber AND Q1b.consec_id2 = Q1a.consec_id1 - 1
WHERE (Q1b.consec_id2 IS NULL
OR Q1a.method<>'F'
OR Q1a.method <> Q1b.method
OR RN=1)
)
, Q2 AS (SELECT Q11.serialnumber
, Q11.mode
, Q11.method
, Q11.starttime
, (SELECT TOP 1 starttime FROM Q11 Q2 WHERE Q2.starttime>Q11.starttime AND Q2.SERIALNUMBER=Q11.SERIALNUMBER ORDER BY Q2.starttime) AS endtime
, RN
FROM Q11
)
SELECT Q2.serialnumber, Q2.mode, Q2.method, Q2.starttime, Q2.endtime
FROM Q2
WHERE NOT (method='F' AND endtime IS NULL)
ORDER BY Q2.serialnumber, Q2.starttime;
输出:
serialnumber mode method starttime endtime
------------ ---- ------ ----------- -----------
2848 R P 1504725241 1504725365
2848 R F 1504725365 1504727651
2848 R P 1504727651 1504727741
2848 R F 1504727741 1504728165
2848 R E 1504728165 1504728167
2848 L B 1504728167 1504728171
2848 T B 1504728171 1504728193
2848 T P 1504728171 1504728193
2848 T F 1504728193 1504728208
2848 L B 1504728208 1504728215
2848 T E 1504728208 1504728215
2848 T F 1504728208 1504728215
2848 L B 1504728215 1504728221
2848 L B 1504728221 1504728225
2848 L B 1504728225 1504728230
2848 L B 1504728230 1504728234
2848 L B 1504728234 1504728238
2848 L B 1504728238 1504728249
2848 L B 1504728249 1504728255
2848 L B 1504728255 1504728300
2848 L B 1504728300 1504728845
2848 S S 1504728845 1504728846
2848 S S 1504728846 NULL
6363 R B 1504726264 1504726265
6363 R F 1504726265 1504729288
6363 R E 1504729288 1504729289
6363 L B 1504729289 1504729318
6363 R B 1504729318 1504731181
6363 R F 1504729318 1504731181
53344 L B 1504725984 1504725987
53344 L B 1504725987 1504725988
53344 L B 1504725988 1504725992
53344 L B 1504725992 1504725993
53344 L B 1504725993 NULL
78901 L B 1504725485 1504725488
78901 L B 1504725488 1504725491
78901 L B 1504725491 NULL
更新 20171009 -- 目前仅适用于 2848
我修改了之前的查询(见底部)以解决连续 F1.0 的要求,Fx.xx,.., F1.0(id est,你可以从 F1.0 开始和结束一个序列Fs)。
我认为,如果有一天您将迁移到 MSSQL2008+,则可能会找到更好的解决方案。以下应该适用于 MSSQL2005。
关于性能:
我在你的数据集中添加了 PK (EVENTS_PK PRIMARY KEY (serialnumber, mode, method, [time]),我添加了一个索引 (INDEX EVENTS_IXN01 ON EVENTS (SERIALNUMBER, TIME)).
实际上,对于您的示例数据,我的查询仅使用索引 EVENTS_IXN01(但如果您不创建 PK,我认为您应该在其中包含一些列)。
我认为还可以做一些其他改进(例如,如果您将方法的第一个字母与其余部分分开)。
如果您决定只查询一段时间 windows(在这种情况下您需要 change/create 索引),可以获得其他改进
WITH Q1 AS (SELECT *
FROM (SELECT serialnumber, [time] AS TTIME, mode, LEFT(method,1) AS method
FROM events
WHERE (LEFT(method,1)<>'F' OR SUBSTRING(method,2,3)='1.0')
AND serialnumber=02848 /* test*/
)A
)
, Q2 AS (SELECT Q1.serialnumber
, Q1.mode
, Q1.method
, Q1.TTIME AS starttime
, (SELECT TOP 1 TTIME FROM Q1 Q2 WHERE Q2.TTIME>Q1.TTIME AND Q2.SERIALNUMBER=Q1.SERIALNUMBER ORDER BY Q2.TTIME) AS endtime
FROM Q1
)
SELECT Q4.serialnumber
,Q4.mode
,Q4.method
,Q4.starttime
,Q4.endtime
FROM (
SELECT Q2.serialnumber
,Q2.mode
,Q2.method
,Q2.starttime
,Q2.endtime+COALESCE(Q3.endtime-Q3.starttime,0) AS endtime
,Q3.starttime AS dupF_starttime
FROM Q2
LEFT JOIN Q2 Q3 ON Q2.serialnumber=Q3.serialnumber AND Q2.mode=Q3.mode AND Q2.method=Q3.method AND Q2.endtime=Q3.starttime AND Q2.method='F'
) Q4
WHERE dupF_starttime IS NULL
ORDER BY serialnumber, starttime;
输出
serialnumber mode method starttime endtime
------------ ---- ------ ----------- -----------
2848 R P 1504725241 1504725365
2848 R F 1504725365 1504727651
2848 R P 1504727651 1504727741
2848 R F 1504727741 1504728165
2848 R E 1504728165 1504728167
2848 L B 1504728167 1504728171
2848 T B 1504728171 1504728193
2848 T P 1504728171 1504728193
2848 L B 1504728208 1504728215
2848 T E 1504728208 1504728215
2848 T F 1504728208 1504728215
2848 L B 1504728215 1504728221
2848 L B 1504728221 1504728225
2848 L B 1504728225 1504728230
2848 L B 1504728230 1504728234
2848 L B 1504728234 1504728238
2848 L B 1504728238 1504728249
2848 L B 1504728249 1504728255
2848 L B 1504728255 1504728300
2848 L B 1504728300 1504728845
2848 S S 1504728845 1504728846
2848 S S 1504728846 NULL
6363 R B 1504726264 1504726265
6363 R F 1504728525 1504729288
6363 R E 1504729288 1504729289
6363 L B 1504729289 1504729318
6363 R B 1504729318 1504729334
6363 R F 1504730579 NULL
53344 L B 1504725984 1504725987
53344 L B 1504725987 1504725988
53344 L B 1504725988 1504725992
53344 L B 1504725992 1504725993
53344 L B 1504725993 NULL
78901 L B 1504725485 1504725488
78901 L B 1504725488 1504725491
78901 L B 1504725491 NULL
上一个
这不是一个确定的答案。这是我找到 post 的唯一方法。
它不尊重您在评论中观察到的观点 ("While assuming the first is always 'F1.0', you can't assume the last is never 'F1.0'")。
此刻(我现在必须离开)我假设在 F 的序列中 F1.0 不能重复(我知道,这不是你要问的)。
所以我只使用序列号 02848(我从 SQLFiddle 上的示例数据中删除了行 (02848, 1504728208, 'T', 'F1.0'))。
也许星期一我可以继续处理查询,或者它可以给你一些建议。
WITH Q1 AS (SELECT *
FROM (SELECT serialnumber, [time] AS TTIME, mode, LEFT(method,1) AS method
FROM events
WHERE 1=1
AND (LEFT(method,1)<>'F' OR SUBSTRING(method,2,3)='1.0')
AND serialnumber=02848 /* test*/
)A
)
SELECT Q1.serialnumber
, Q1.mode
, Q1.method
, Q1.TTIME AS starttime
, (SELECT TOP 1 TTIME FROM Q1 Q2 WHERE Q2.TTIME>Q1.TTIME AND Q2.SERIALNUMBER=Q1.SERIALNUMBER ORDER BY Q2.TTIME) AS endtime
FROM Q1
ORDER BY Q1.serialnumber, Q1.TTIME
;
我看到使用(但我不能做很多其他测试)的性能改进:
CREATE INDEX EVENTS_IXN01 ON EVENTS (SERIALNUMBER, TIME);
输出:
serialnumber mode method starttime endtime
------------ ---- ------ ----------- -----------
2848 R P 1504725241 1504725365
2848 R F 1504725365 1504727651
2848 R P 1504727651 1504727741
2848 R F 1504727741 1504728165
2848 R E 1504728165 1504728167
2848 L B 1504728167 1504728171
2848 T B 1504728171 1504728193
2848 T P 1504728171 1504728193
2848 T F 1504728193 1504728208
2848 T E 1504728208 1504728215
2848 L B 1504728208 1504728215
2848 L B 1504728215 1504728221
2848 L B 1504728221 1504728225
2848 L B 1504728225 1504728230
2848 L B 1504728230 1504728234
2848 L B 1504728234 1504728238
2848 L B 1504728238 1504728249
2848 L B 1504728249 1504728255
2848 L B 1504728255 1504728300
2848 L B 1504728300 1504728845
2848 S S 1504728845 1504728846
2848 S S 1504728846 NULL
TL;DR:我正在尝试从传入的事件列表中删除特定类型事件的重复项。我有 5-20 百万条记录。应删除缩进行:
sn# time mo method
02848 1504725241 R P
02848 1504725365 R F1.0
02848 1504725366 R F1.6
02848 1504725366 R F2.0
02848 1504727651 R P
02848 1504727681 R P
02848 1504727741 R F1.0
02848 1504728165 R E
详细说明:
为了提供一些上下文,这些是执行的用户操作。其中模式是用户正在使用的服务类型。而方法就是用户所做的。比如P是pause的缩写,F是fast forward的缩写
我正在研究的 table 具有以下结构:
CREATE TABLE events
([serialnumber] int, [time] int, [mode] varchar(1), [method] varchar(4))
它每小时更新一次,包含 5 到 2000 万条记录。 示例数据如上所示。
我希望创建会话以填写以下内容table:
CREATE TABLE sessions(serialnumber int, mode varchar(1), method varchar(1), startTime int, endTime int);
我想要实现的是基于这些事件创建会话,并使用一个重要的过滤器。我不想使用方法 F 重复事件,我只想要第一个 F 事件,连接到任何非 F 接下来是活动。也就是说,对于以上我想在处理后有以下会话,即我想要的结果是:
#sn metho mod starttime endtime
02848 R P 1504725241 1504725365
02848 R F 1504725365 1504727651
02848 R P 1504727651 1504727681
02848 R P 1504727681 1504727741
02848 R F 1504727741 1504728165
这里F1.0、F1.6、F2.0等都改成了F,保留一组F事件的第一个时间戳,去掉了重复。由于大量记录,我无法有效地执行此操作。
截至目前,我正在尝试以下操作:
- 当 table 更新时,删除所有 F 事件,除了每组 F 事件中的第一个事件, pr序列号。
- 使用
cross apply
很像LAG
函数来生成会话列表。
完整的 SQL 代码如下所示:
--Delete recurring F events
DELETE outside
FROM events AS outside
CROSS APPLY
(
SELECT TOP 1 inside.[Time]
FROM events inside
WHERE outside.serialnumber = inside.serialnumber
AND (outside.[Time] > inside.[Time]
OR (outside.[Time] = inside.[Time]
AND outside.[method] != inside.[method]
)
)
AND LEFT(outside.[method],1) = 'F'
AND LEFT(inside.[method],1) = 'F'
AND NOT EXISTS
(SELECT innerInside.[Time] FROM events innerInside
WHERE innerInside.serialnumber = inside.serialnumber
AND innerInside.[Time] <= outside.[Time]
AND innerInside.[Time] > inside.[Time]
AND LEFT(innerInside.[method],1) != 'F'
)
ORDER BY inside.[Time] DESC
) processed_inside
--Create sessions from events
SELECT serialnumber
,outside.[mode]
,LEFT(outside.[method],1) AS 'Method'
,outside.[Time] AS 'SessionStart'
,processed_inside.[Time] AS 'SessionEnd'
FROM events AS outside
CROSS APPLY
(
SELECT MIN( inside.[Time]) AS 'Time'
FROM events AS inside
WHERE outside.serialnumber = inside.serialnumber
AND outside.[Time] < inside.[Time]
) processed_inside
WHERE processed_inside.[Time] IS NOT NULL
这可行,但删除步骤慢得无法忍受。这是删除步骤的实际执行计划的图片。
我也考虑过将第 1 步作为第 2 步的一部分进行,但尚未找到任何有意义的方法。最后,我正在寻找一种方法来有效地创建会话而不生成太多会话(因此删除类型 F 的事件)。
如果您一路走到这里,可以在这里找到 sqlfiddle:http://sqlfiddle.com/#!6/a5392/1。我在其中添加了上述两个功能。输出 table 是期望的结果。
如果您能给我任何帮助,我将不胜感激。
第二次更新2017100915:35
这应该可以处理您所有的示例数据。
关于旧版本附加组件的一些解释:
Q1查询中的last条件(OR(RN=1 AND method='F')是最后离开 F(因为后者对于计算聚合 F 的结束时间很有用);
在 Q11 中我使用了两个 ROW_NUMBER() 来查找连续的方法和
WHERE条件中排除除 最后一个(如果存在)- 在 WITH 内的最后一个查询 (Q2) 中,结束时间是使用 (SELECT TOP 1 ....)
- 最后一个 WHERE 条件是删除(如果存在)结尾(但 已经累计)F法(见上文第1点)
- 也看我之前版本说的
WITH Q1 AS (SELECT *
FROM (SELECT serialnumber, [time] AS TTIME, mode, LEFT(method,1) AS method, SUBSTRING(method,2,3) AS rest_of_method
, ROW_NUMBER() OVER (PARTITION BY serialnumber ORDER BY [time] DESC) AS RN
FROM events
)A
WHERE method<>'F' OR rest_of_method='1.0' OR (RN=1 AND method='F')
/* AND serialnumber=6363*/ /* test*/
)
, Q11 AS (SELECT Q1a.serialnumber, Q1a.mode, Q1a.method, Q1a.starttime, RN
FROM (SELECT serialnumber, mode, method, TTIME AS starttime
, ROW_NUMBER() OVER (PARTITION BY serialnumber ORDER BY TTIME, method) AS consec_id1
, RN
FROM Q1) Q1a
LEFT JOIN (SELECT serialnumber, mode, method, TTIME AS starttime
, ROW_NUMBER() OVER (PARTITION BY serialnumber ORDER BY TTIME, method) AS consec_id2
FROM Q1) Q1b on Q1b.serialnumber=Q1a.serialnumber AND Q1b.consec_id2 = Q1a.consec_id1 - 1
WHERE (Q1b.consec_id2 IS NULL
OR Q1a.method<>'F'
OR Q1a.method <> Q1b.method
OR RN=1)
)
, Q2 AS (SELECT Q11.serialnumber
, Q11.mode
, Q11.method
, Q11.starttime
, (SELECT TOP 1 starttime FROM Q11 Q2 WHERE Q2.starttime>Q11.starttime AND Q2.SERIALNUMBER=Q11.SERIALNUMBER ORDER BY Q2.starttime) AS endtime
, RN
FROM Q11
)
SELECT Q2.serialnumber, Q2.mode, Q2.method, Q2.starttime, Q2.endtime
FROM Q2
WHERE NOT (method='F' AND endtime IS NULL)
ORDER BY Q2.serialnumber, Q2.starttime;
输出:
serialnumber mode method starttime endtime
------------ ---- ------ ----------- -----------
2848 R P 1504725241 1504725365
2848 R F 1504725365 1504727651
2848 R P 1504727651 1504727741
2848 R F 1504727741 1504728165
2848 R E 1504728165 1504728167
2848 L B 1504728167 1504728171
2848 T B 1504728171 1504728193
2848 T P 1504728171 1504728193
2848 T F 1504728193 1504728208
2848 L B 1504728208 1504728215
2848 T E 1504728208 1504728215
2848 T F 1504728208 1504728215
2848 L B 1504728215 1504728221
2848 L B 1504728221 1504728225
2848 L B 1504728225 1504728230
2848 L B 1504728230 1504728234
2848 L B 1504728234 1504728238
2848 L B 1504728238 1504728249
2848 L B 1504728249 1504728255
2848 L B 1504728255 1504728300
2848 L B 1504728300 1504728845
2848 S S 1504728845 1504728846
2848 S S 1504728846 NULL
6363 R B 1504726264 1504726265
6363 R F 1504726265 1504729288
6363 R E 1504729288 1504729289
6363 L B 1504729289 1504729318
6363 R B 1504729318 1504731181
6363 R F 1504729318 1504731181
53344 L B 1504725984 1504725987
53344 L B 1504725987 1504725988
53344 L B 1504725988 1504725992
53344 L B 1504725992 1504725993
53344 L B 1504725993 NULL
78901 L B 1504725485 1504725488
78901 L B 1504725488 1504725491
78901 L B 1504725491 NULL
更新 20171009 -- 目前仅适用于 2848
我修改了之前的查询(见底部)以解决连续 F1.0 的要求,Fx.xx,.., F1.0(id est,你可以从 F1.0 开始和结束一个序列Fs)。 我认为,如果有一天您将迁移到 MSSQL2008+,则可能会找到更好的解决方案。以下应该适用于 MSSQL2005。
关于性能: 我在你的数据集中添加了 PK (EVENTS_PK PRIMARY KEY (serialnumber, mode, method, [time]),我添加了一个索引 (INDEX EVENTS_IXN01 ON EVENTS (SERIALNUMBER, TIME)).
实际上,对于您的示例数据,我的查询仅使用索引 EVENTS_IXN01(但如果您不创建 PK,我认为您应该在其中包含一些列)。
我认为还可以做一些其他改进(例如,如果您将方法的第一个字母与其余部分分开)。
如果您决定只查询一段时间 windows(在这种情况下您需要 change/create 索引),可以获得其他改进
WITH Q1 AS (SELECT *
FROM (SELECT serialnumber, [time] AS TTIME, mode, LEFT(method,1) AS method
FROM events
WHERE (LEFT(method,1)<>'F' OR SUBSTRING(method,2,3)='1.0')
AND serialnumber=02848 /* test*/
)A
)
, Q2 AS (SELECT Q1.serialnumber
, Q1.mode
, Q1.method
, Q1.TTIME AS starttime
, (SELECT TOP 1 TTIME FROM Q1 Q2 WHERE Q2.TTIME>Q1.TTIME AND Q2.SERIALNUMBER=Q1.SERIALNUMBER ORDER BY Q2.TTIME) AS endtime
FROM Q1
)
SELECT Q4.serialnumber
,Q4.mode
,Q4.method
,Q4.starttime
,Q4.endtime
FROM (
SELECT Q2.serialnumber
,Q2.mode
,Q2.method
,Q2.starttime
,Q2.endtime+COALESCE(Q3.endtime-Q3.starttime,0) AS endtime
,Q3.starttime AS dupF_starttime
FROM Q2
LEFT JOIN Q2 Q3 ON Q2.serialnumber=Q3.serialnumber AND Q2.mode=Q3.mode AND Q2.method=Q3.method AND Q2.endtime=Q3.starttime AND Q2.method='F'
) Q4
WHERE dupF_starttime IS NULL
ORDER BY serialnumber, starttime;
输出
serialnumber mode method starttime endtime
------------ ---- ------ ----------- -----------
2848 R P 1504725241 1504725365
2848 R F 1504725365 1504727651
2848 R P 1504727651 1504727741
2848 R F 1504727741 1504728165
2848 R E 1504728165 1504728167
2848 L B 1504728167 1504728171
2848 T B 1504728171 1504728193
2848 T P 1504728171 1504728193
2848 L B 1504728208 1504728215
2848 T E 1504728208 1504728215
2848 T F 1504728208 1504728215
2848 L B 1504728215 1504728221
2848 L B 1504728221 1504728225
2848 L B 1504728225 1504728230
2848 L B 1504728230 1504728234
2848 L B 1504728234 1504728238
2848 L B 1504728238 1504728249
2848 L B 1504728249 1504728255
2848 L B 1504728255 1504728300
2848 L B 1504728300 1504728845
2848 S S 1504728845 1504728846
2848 S S 1504728846 NULL
6363 R B 1504726264 1504726265
6363 R F 1504728525 1504729288
6363 R E 1504729288 1504729289
6363 L B 1504729289 1504729318
6363 R B 1504729318 1504729334
6363 R F 1504730579 NULL
53344 L B 1504725984 1504725987
53344 L B 1504725987 1504725988
53344 L B 1504725988 1504725992
53344 L B 1504725992 1504725993
53344 L B 1504725993 NULL
78901 L B 1504725485 1504725488
78901 L B 1504725488 1504725491
78901 L B 1504725491 NULL
上一个
这不是一个确定的答案。这是我找到 post 的唯一方法。
它不尊重您在评论中观察到的观点 ("While assuming the first is always 'F1.0', you can't assume the last is never 'F1.0'")。
此刻(我现在必须离开)我假设在 F 的序列中 F1.0 不能重复(我知道,这不是你要问的)。 所以我只使用序列号 02848(我从 SQLFiddle 上的示例数据中删除了行 (02848, 1504728208, 'T', 'F1.0'))。
也许星期一我可以继续处理查询,或者它可以给你一些建议。
WITH Q1 AS (SELECT *
FROM (SELECT serialnumber, [time] AS TTIME, mode, LEFT(method,1) AS method
FROM events
WHERE 1=1
AND (LEFT(method,1)<>'F' OR SUBSTRING(method,2,3)='1.0')
AND serialnumber=02848 /* test*/
)A
)
SELECT Q1.serialnumber
, Q1.mode
, Q1.method
, Q1.TTIME AS starttime
, (SELECT TOP 1 TTIME FROM Q1 Q2 WHERE Q2.TTIME>Q1.TTIME AND Q2.SERIALNUMBER=Q1.SERIALNUMBER ORDER BY Q2.TTIME) AS endtime
FROM Q1
ORDER BY Q1.serialnumber, Q1.TTIME
;
我看到使用(但我不能做很多其他测试)的性能改进:
CREATE INDEX EVENTS_IXN01 ON EVENTS (SERIALNUMBER, TIME);
输出:
serialnumber mode method starttime endtime
------------ ---- ------ ----------- -----------
2848 R P 1504725241 1504725365
2848 R F 1504725365 1504727651
2848 R P 1504727651 1504727741
2848 R F 1504727741 1504728165
2848 R E 1504728165 1504728167
2848 L B 1504728167 1504728171
2848 T B 1504728171 1504728193
2848 T P 1504728171 1504728193
2848 T F 1504728193 1504728208
2848 T E 1504728208 1504728215
2848 L B 1504728208 1504728215
2848 L B 1504728215 1504728221
2848 L B 1504728221 1504728225
2848 L B 1504728225 1504728230
2848 L B 1504728230 1504728234
2848 L B 1504728234 1504728238
2848 L B 1504728238 1504728249
2848 L B 1504728249 1504728255
2848 L B 1504728255 1504728300
2848 L B 1504728300 1504728845
2848 S S 1504728845 1504728846
2848 S S 1504728846 NULL