SQL для выбора событий, которые произошли в одно и то же время - PullRequest
2 голосов
/ 04 октября 2010

У меня есть таблица базы данных событий, которые произошли:

(timestamp, other data...)

Я хочу сгруппировать их по тем вещам, которые произошли в одно и то же время. То есть при упорядочении по метке времени, так что все события в каждой группе находятся в пределах X секунд (например, X = 3) от какого-либо другого события в этой группе и более чем на X секунд от всех событий в других группах.

Есть ли способ сделать это даже несколько эффективно в SQL, или я должен просто ORDER BY timestamp, вставить данные в мое приложение и сделать это там?

Ответы [ 2 ]

1 голос
/ 04 октября 2010

Иногда я хочу сделать что-то подобное с некоторыми журналами доступа, которые мы имеем. Мои данные выглядят так:

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 | 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. Затем вы можете использовать этот номер для идентификации записей в сеансе, группировки по ним и т. Д.

В 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
;
1 голос
/ 04 октября 2010

Вы можете использовать UNIX_TIMESTAMP и DIV для вычисления значений, которые были бы одинаковыми для событий, которые произошли "одновременно" .

Ниже подсчитывается количествособытия с 10-секундными интервалами:

SELECT UNIX_TIMESTAMP(timestamp) DIV 10, COUNT(*)
FROM events
GROUP BY 1;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...