Sql 根据时间、id和事件类型从未排序的事件表中删除某些事件

Sql 根据时间、id和事件类型从未排序的事件表中删除某些事件,sql,tsql,sql-server-2005,Sql,Tsql,Sql Server 2005,TL;DR:我正在尝试从传入事件列表中删除某类事件的重复。我有500-2000万张唱片。应删除缩进线: 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

TL;DR:我正在尝试从传入事件列表中删除某类事件的重复。我有500-2000万张唱片。应删除缩进线:

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是暂停的缩写,F是快进的缩写

我正在处理的表具有以下结构:

CREATE TABLE events
([serialnumber] int, [time] int, [mode] varchar(1), [method] varchar(4))
它每小时更新500万到2000万条记录。 使用上面的示例数据

我希望创建会话以填写下表:

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事件的第一个时间戳保留,重复已删除。由于有大量的记录,我很难有效地做到这一点

截至目前,我正在尝试以下方法:

更新表时,删除所有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类型的事件

如果您一直在这里,可以在这里找到SQLFIDLE:。在这里,我添加了上述两个函数。输出表是所需的结果

如果您能给我任何帮助,我将不胜感激。

第二次更新20171009 15:35

这应该可以处理所有的样本数据。 关于先前版本的附加组件的一些说明:

Q1查询或RN=1和方法='F'中的最后一个条件是离开last F,因为后者对于计算聚合F的结束时间很有用

在Q11中,我使用两行_数来查找连续的方法和 在WHERE条件中,除了 最后一个,如果有的话

在带有的最后一个查询Q2中,使用SELECT TOP计算结束时间 1. 最后一个WHERE条件是如果出现结束但 已累积的F方法见上文第1点 也看看我在以前的版本中说了些什么。 更新20171009-目前仅适用于2848

我修改了前面的查询请参见底部,以解决连续F1.0、Fx.xx、…、F1.0 id est的必要条件,您可以使用F1.0开始和结束一系列Fs。 我认为,如果有一天您将迁移到MSSQLS2008+,可能会找到更好的解决方案。以下内容应适用于MSSQL2005

关于性能: 我将PK添加到数据集EVENTS_PK主键serialnumber、mode、method、[time],我在EVENTS serialnumber、time上添加了索引EVENTS_IXN01

实际上,对于您的示例数据,我的查询只使用index EVENTS_IXN01,但是如果您不创建PK,我认为您应该在其中包含一些列。 我认为可以做一些其他的改进,例如,如果你把方法的第一个字母从其余字母中分离出来。 如果您决定只查询一段时间窗口,则可以获得其他改进。在这种情况下,您需要更改/创建索引

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;
先前的

这不是一个明确的答案。这是我找到的唯一张贴它的方法

它不尊重您在评论中观察到的点,而假设第一个始终是“F1.0”,您不能假设最后一个永远不是“F1.0”

现在我必须离开了,我假设在一系列的Fs中F1.0是不能重复的,我知道,这不是你们要问的。 因此,我只对序列号02848使用,在这里,我从SQLFIDLE上的示例数据中删除了行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
;
输出:

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
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

并没有索引吗?对于那个些编辑问题的人:请阅读文本:他想给缩进提供证据lines@SqlZim不,但我可以创建它们,只要为创建表而进行的大容量插入不会比我需要执行的操作慢。@ewolden:您需要删除事件还是只用于解决问题?我的意思是,如果一个人可以根据请求生成会话而不删除事件,这对你有好处吗?@etsa如果你可以生成会话
如果不删除,那将是完美的。这看起来很有希望,并为我激发了一些想法。我星期一以前也不在。将报告此操作的执行情况,以及我是否找到了消除结果中出现的后续“F1.0”的方法。谢谢大家!@eWalden-添加了另一个查询版本,该版本将删除后续的F1.0。添加了一些关于性能的说明。@ewolden-添加了另一个第三个版本:它应该可以按要求工作。性能比以前差了一点……非常感谢您花费的所有时间和精力。我真的很感激。我已经使用了您的上一次迭代,并且看到了效率的巨大提高。再次感谢你!不客气。请记住,如果有一天您都迁移到SQLServer2008+,查询可能会简化。
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