SQL用于选择几乎同时发生的事件

时间:2010-10-04 16:06:39

标签: sql mysql group-by

我有一个发生事件的数据库表:

(timestamp, other data...)

我想通过“几乎同时”发生的事情对这些事件进行分组。也就是说,当按时间戳排序时,每个组中的所有事件都在该组中某个其他事件的X秒(例如,X = 3)内,并且与其他组中的所有事件相距超过X秒。

有没有办法在SQL中有效地执行此操作,或者我应该只是按时间戳排序,将数据拉入我的应用程序,并在那里执行此操作?

2 个答案:

答案 0 :(得分:1)

您可以使用UNIX_TIMESTAMPDIV来计算与“同时”发生的事件相同的值。

以下计算10秒间隔内的事件数量:

SELECT UNIX_TIMESTAMP(timestamp) DIV 10, COUNT(*)
FROM events
GROUP BY 1;

答案 1 :(得分:1)

有时我想用我们拥有的一些访问日志来做这样的事情。我的数据如下:

EventID | UserID | When                | What
--------|--------|---------------------|--------
  7477  |   33   | 20090614:140517.131 | ...
  7478  |   33   | 20090614:140518.992 | ...
  7479  |   33   | 20090614:140522.020 | ...
  7480  |   33   | 20090614:142719.001 | ...
  7481  |   33   | 20090614:142720.668 | ...

然后我想通过userid确定一个“会话”以及时间是否“乱”,这就是我在阅读你的陈述的方式。所以,从上面来看:

 UserId | SessionStart       | Stuff
--------|--------------------|---------
   33   | 6/14/2009 14:05:17 | ...
   33   | 6/14/2009 14:27:19 | ...

我在SQL中使用SQL Server执行此操作。我在这种情况下的策略是:

  1. 按用户分组
  2. 确定每行两条记录之间的差异。
  3. 如果增量超过我的阈值,则将IsNewSession列设为1,否则为0.此记录是新会话的时间/日期。
  4. 创建一个SessionNumber列,它是IsNewSession的运行总计。然后,您可以使用此编号来标识会话中的记录,对其进行分组等。
  5. 在SQL Server中,使用临时表,它非常快。使用单个SQL语句,它很快变得非常慢。在这两种情况下,它都非常难看。另一方面,Oracle有一套很好的分析函数来处理增量和运行总量,使代码更清晰(通常)更快。

    如果mysql没有任何这样的魔力,并且你的团队并不特别迷恋SQL,我建议你只考虑在你的应用程序中使用可维护的生产代码。

    以下是我正在使用的消毒版本。如果您需要“单个SQL语句”版本,请告诉我。抱歉给你SQL Server代码而不是mysql。 :)

    -- Set up work table
    DROP TABLE #temp
    CREATE TABLE #temp
    (
        ID INT PRIMARY KEY,
        EventDate DATETIME,
        RecordRank INT,
        IsNewSession INT,
        SessionNum INT
    );
    
    DECLARE
        @NumSecondsBetweenSessions INT,
        @StartDate DATETIME,
        @EndDate DATETIME
    ;
    
    SELECT
        @NumSecondsBetweenSessions = 600,
        @StartDate = '20000101',
        @EndDate = '20201231'
    ;
    
    -- Set up what will be our "Current" records in the "Current vs
    -- Previous" comparision.
    INSERT INTO #temp
    (
        ID,
        EventDate,
        RecordRank,
        IsNewSession,
        SessionNum
    )
    SELECT
        SL.ID,
        SL.Created_DateTime,
        ROW_NUMBER() OVER (ORDER BY SL.Created_DateTime ASC) AS RecordRank,
        0,
        0
    FROM
        SystemLog SL
    WHERE
        SL.Created_DateTime BETWEEN @StartDate and @EndDate
    ;
    
    -- Checking the time delta between the Current and Previous
    -- records to see if we have a new session.
    UPDATE #temp
    SET
        IsNewSession = 
            CASE
                WHEN PrevT.EventDate IS NULL THEN 1
                WHEN DATEDIFF(s, PrevT.EventDate, #temp.EventDate) > @NumSecondsBetweenSessions THEN 1
                ELSE 0
            END
    FROM
        #temp
        LEFT OUTER JOIN #temp PrevT
        ON #temp.RecordRank = (PrevT.RecordRank + 1)
    ;
    
    -- This is performing a "running total" on IsNewSession to assign
    -- records to a specific Session.
    DECLARE @SessionNum INT;
    SET @SessionNum = 0;
    UPDATE #temp
    SET
        @SessionNum = @SessionNum + IsNewSession,
        SessionNum = @SessionNum
    ;
    
    -- The results.
    SELECT
        T.*,
        SL.*
    FROM
        #temp T
        JOIN SystemLog SL
        ON SL.ID = T.ID
    ORDER BY
        RecordRank ASC
    ;