【问题标题】:SQL to select events that happened at pretty much the same timeSQL 选择几乎同时发生的事件
【发布时间】:2011-04-20 21:30:47
【问题描述】:

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

(timestamp, other data...)

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

有没有办法在 SQL 中更有效地做到这一点,或者我应该只按时间戳排序,将数据拉入我的应用程序,然后在那里执行?

【问题讨论】:

  • 样本数据和预期结果会非常有用。
  • 以定义的时间间隔彼此间隔三秒:00:00:00 到 00:00:03,等等?或者那个发生在 00:00:05,它需要在 00:00:08 与另一个相关
  • 以下说法正确吗?一个事件组必须至少包含两个事件。一个事件组的所有成员都在该组的另一个成员的 3 秒内。没有一个事件组的成员与另一个组中的事件的距离小于 10 秒。

标签: sql mysql group-by


【解决方案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 | ...

然后我想通过用户 ID 识别一个“会话”以及时间是否“块”,这就是我阅读您的陈述的方式。所以,从上面来看:

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

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

  1. 按用户分组
  2. 确定每行两条记录之间的差异。
  3. 如果增量超过我的阈值,则创建一个 IsNewSession 列,否则为 0。此记录是新会话的时间/日期。
  4. 创建一个 SessionNumber 列,它是 IsNewSession 的运行总数。然后,您可以使用此编号来识别会话中的记录、对它们进行分组等。

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2016-10-19
      • 1970-01-01
      • 1970-01-01
      • 2017-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-19
      • 1970-01-01
      相关资源
      最近更新 更多