Подсчитайте количество строк, которые не находятся в пределах 10 секунд друг от друга

Я отслеживаю посетителей сети. Я сохраняю IP-адрес, а также временную метку посещения.

ip_address    time_stamp
180.2.79.3  1301654105
180.2.79.3  1301654106
180.2.79.3  1301654354
180.2.79.3  1301654356
180.2.79.3  1301654358
180.2.79.3  1301654366
180.2.79.3  1301654368
180.2.79.3  1301654422

У меня есть запрос на получение общих треков:

SELECT COUNT(*) AS tracks FROM tracking

Однако теперь я хочу игнорировать посещения пользователей, которые посещали несколько раз за 10 секунд каждого посещения. Поскольку я не рассматриваю этот очередной визит, его еще часть первого визита.

Когда ip_address тот же, проверьте отметка времени и только считать эти строки которые находятся в 10 секундах от каждого другие.

Мне сложно помещать это в форму запроса SQL, я был бы признателен за любую помощь в этом!

Ответ 1

Позвольте мне начать с этой таблицы. Я буду использовать обычные временные метки, чтобы мы могли легко понять, что происходит.

180.2.79.3   2011-01-01 08:00:00
180.2.79.3   2011-01-01 08:00:09
180.2.79.3   2011-01-01 08:00:20
180.2.79.3   2011-01-01 08:00:23
180.2.79.3   2011-01-01 08:00:25
180.2.79.3   2011-01-01 08:00:40
180.2.79.4   2011-01-01 08:00:00
180.2.79.4   2011-01-01 08:00:13
180.2.79.4   2011-01-01 08:00:23
180.2.79.4   2011-01-01 08:00:25
180.2.79.4   2011-01-01 08:00:27
180.2.79.4   2011-01-01 08:00:29
180.2.79.4   2011-01-01 08:00:50

Если я правильно вас понимаю, вы хотите считать их такими.

180.2.79.3   3
180.2.79.4   3

Вы можете сделать это для каждого ip_address, выбрав максимальную метку времени, которая является как

  • больше, чем текущая строка метка времени и
  • меньше или равно 10 секундам, превышающим текущую временную метку строки.

Взятие этих двух критериев вместе приведет к некоторым нулям, которые окажутся действительно полезными.

select ip_address, 
       t_s.time_stamp, 
       (select max(t.time_stamp) 
        from t_s t 
        where t.ip_address = t_s.ip_address 
          and t.time_stamp > t_s.time_stamp
          and t.time_stamp - t_s.time_stamp <= interval '10' second) next_page
from t_s 
group by ip_address, t_s.time_stamp
order by ip_address, t_s.time_stamp;

ip_address   time_stamp            next_page
180.2.79.3   2011-01-01 08:00:00   2011-01-01 08:00:09
180.2.79.3   2011-01-01 08:00:09   <null>
180.2.79.3   2011-01-01 08:00:20   2011-01-01 08:00:25
180.2.79.3   2011-01-01 08:00:23   2011-01-01 08:00:25
180.2.79.3   2011-01-01 08:00:25   <null>
180.2.79.3   2011-01-01 08:00:40   <null>
180.2.79.4   2011-01-01 08:00:00   <null>
180.2.79.4   2011-01-01 08:00:13   2011-01-01 08:00:23
180.2.79.4   2011-01-01 08:00:23   2011-01-01 08:00:29
180.2.79.4   2011-01-01 08:00:25   2011-01-01 08:00:29
180.2.79.4   2011-01-01 08:00:27   2011-01-01 08:00:29
180.2.79.4   2011-01-01 08:00:29   <null>
180.2.79.4   2011-01-01 08:00:50   <null>

Временная метка, которая знаменует конец посещения, имеет значение null для собственной следующей_страницы. Это потому, что временная метка меньше или равна time_stamp + 10 секунд для этой строки.

Чтобы получить счет, я бы, вероятно, создал представление и подсчитал нули.

select ip_address, count(*)
from t_s_visits 
where next_page is null
group by ip_address

180.2.79.3   3
180.2.79.4   3

Ответ 2

Вы можете JOIN отслеживать таблицу сами по себе и отфильтровывать записи, которые вам не нужны, добавляя предложение WHERE.

SELECT  t1.ip_address
        , COUNT(*) AS tracks
FROM    tracking t1
        LEFT OUTER JOIN tracking t2 ON t2.ip_address = t1.ip_address
                                       AND t2.time_stamp < t1.time_stamp + 10
WHERE   t2.ip_adress IS NULL
GROUP BY
        t1.ip_address

Edit

После script работает в SQL Server, но я не могу выразить его в одном выражении SQL, не говоря уже о его преобразовании в MySQL. Это может дать вам несколько указаний на то, что нужно, хотя.

Примечание. Я предполагаю, что для данных входов следует выбирать номера 1 и 11.

;WITH q (number) AS (
  SELECT 1
  UNION ALL SELECT 2
  UNION ALL SELECT 10
  UNION ALL SELECT 11  
  UNION ALL SELECT 12
)
SELECT  q1.Number as n1
        , q2.Number as n2
        , 0 as Done
INTO    #Temp
FROM    q q1
        LEFT OUTER JOIN q q2 ON q2.number < q1.number + 10
                                AND q2.number > q1.number

DECLARE @n1 INTEGER
DECLARE @n2 INTEGER

WHILE EXISTS (SELECT * FROM #Temp WHERE Done = 0)
BEGIN

  SELECT  TOP 1 @n1 = n1
          , @n2= n2
  FROM    #Temp
  WHERE   Done = 0

  DELETE  FROM #Temp
  WHERE   n1 = @n2

  UPDATE  #Temp 
  SET     Done = 1
  WHERE   n1 = @n1 
          AND n2 = @n2         
END        

SELECT  DISTINCT n1 
FROM    #Temp

DROP TABLE #Temp

Ответ 3

Самый простой способ сделать это - разделить временные метки на 10 и подсчитать различные комбинации этих значений и значения ip_address. Таким образом, каждый 10-секундный период подсчитывается отдельно.

Если вы запустите это в своих образцовых данных, он даст вам 4 трека, которые, как вы думаете, будут вам нужны.

Попробуйте и посмотрите, дает ли он желаемые результаты в вашем полном наборе данных:

SELECT COUNT(DISTINCT ip_address, FLOOR(time_stamp/10)) AS tracks 
FROM tracking

Ответ 4

Сделайте левое соединение с записями с тем же самым ip и близким временем и отфильтруйте записи, где есть совпадение:

select count(*) as visits
from (
  select t.ip_address
  from tracking t
  left join tracking t2
    on t2.ip_address = t.ip_address
    and t2.timestamp > t.timestamp and t2.timestamp <= t.timestamp + 10
  where t2.ip_address is null
) x

Ответ 5

Как обычно, с SQL существует множество решений для вашей проблемы. Я бы использовал следующий запрос, который прост и должен быть "достаточно хорошим":

SELECT COUNT(*) AS tracks 
FROM (
    SELECT ip_address 
    FROM tracking 
    GROUP BY ip_address, FLOOR(time_stamp / 10)
)

Группы вспомогательных запросов посещают одного пользователя с интервалом в 10 с, так что они считаются одним посещением.

Из-за этого можно найти случаи, когда два посещения будут отображаться в разных 10-секундном окне, даже если интервал между этими посещениями будет меньше 10 с. Для устранения таких случаев потребуется гораздо более сложная логика, и аналитическая ценность этой добавленной сложности будет сомнительной (интервал 10 секунд звучит как произвольное значение в любом случае).

Ответ 6

Select Z.IP, Count(*) As VisitCount
From    (
        Select V.IP
        From visitors As V
            Left Join visitors As V2
                On V2.IP = V.IP
                    And V2.time_stamp > V.time_stamp
        Group By V.IP, V.time_stamp
        Having (Min(V2.time_stamp) - V.time_stamp) >= 10
        ) As Z
Group By Z.IP

Это подсчет любого посещения, где следующая запись находится на расстоянии более 10 секунд в качестве нового посещения.

Ответ 7

Следующая логика будет считать только посещение "уникальным посещением", если в предыдущие 10 секунд не было предыдущей записи с одного и того же IP-адреса.

Это означает, что {1,11,21,32,42,52,62,72} будет считаться 2 посещениями, соответственно 3 и 5 треков соответственно.

Он выполняет это, сначала определяя уникальные посещения. Затем он подсчитывает все посещения, которые произошли между этим уникальным визитом и следующим уникальным визитом.

WITH
    unique_visits
(
  SELECT
    ip_address, time_stamp
  FROM
    visitors
  WHERE
    NOT EXISTS (SELECT * FROM visitors AS [previous]
                WHERE ip_address  = visitors.ip_address
                  AND time_stamp >= visitors.timestamp - 10
                  AND time_stamp <  visitors.timestamp)
)
SELECT
  unique_visitors.ip_address,
  unique_visitors.time_stamp,
  COUNT(*) AS [total_tracks]
FROM
  unique_visitors
INNER JOIN
  visitors
    ON  visitors.ip_address  = unique_visitors.ip_address
    AND visitors.time_stamp >= unique_visitors.time_stamp
    AND visitors.time_stamp <  ISNULL(
                                  (SELECT MIN(time_stamp) FROM unique_visitors [next]
                                   WHERE  ip_address = unique_visitors.ip_address
                                   AND    time_stamp > unique_visitors.ip_address)
                                  , visitors.time_stamp + 1
                               )

Вам также понадобится индекс или первичный ключ (ip_address, time_stamp)

Ответ 8

Для хихиканья, вот взлом UPDATE, который выполняет то, что вам нужно. Существует множество причин не применять это, в том числе, но не ограничиваясь тем, что он может просто перестать работать. В любом случае, предполагая, что у вас есть таблица, первоначально упорядоченная по ip → timestamp, это должно (обычно) дать вам правильные ответы. Опять же, это для полноты, если вы это реализуете, посмотрите риски ранее.

CREATE TABLE #TestIPs
(
    ip_address varchar(max),
    time_stamp decimal(12,0),
    cnt int
    )

INSERT INTO #TestIPs (ip_address, time_stamp)
SELECT '180.2.79.3',  1301654105 UNION ALL
SELECT '180.2.79.3',  1301654106 UNION ALL
SELECT '180.2.79.3',  1301654354 UNION ALL
SELECT '180.2.79.3',  1301654356 UNION ALL
SELECT '180.2.79.3',  1301654358 UNION ALL
SELECT '180.2.79.3',  1301654366 UNION ALL
SELECT '180.2.79.3',  1301654368 UNION ALL
SELECT '180.2.79.3',  1301654422 UNION ALL
SELECT '180.2.79.4',  1301654105 UNION ALL
SELECT '180.2.79.4',  1301654106 UNION ALL
SELECT '180.2.79.4',  1301654354 UNION ALL
SELECT '180.2.79.4',  1301654356 UNION ALL
SELECT '180.2.79.4',  1301654358 UNION ALL
SELECT '180.2.79.4',  1301654366 UNION ALL
SELECT '180.2.79.4',  1301654368 UNION ALL
SELECT '180.2.79.4',  1301654422

DECLARE @count int; SET @count = 0
DECLARE @ip varchar(max); SET @ip = 'z'
DECLARE @timestamp decimal(12,0); SET @timestamp = 0;

UPDATE #TestIPs
    SET @count = cnt = CASE WHEN time_stamp - @timestamp > 10 THEN @count + 1 ELSE CASE WHEN @ip <> ip_address THEN 1 ELSE @count END END,      
        @timestamp = time_stamp,
        @ip = ip_address


        SELECT ip_address, MAX(cnt) AS 'Visits' FROM #TestIPs GROUP BY ip_address

Результаты:

ip_address  Visits
------------ -----------
180.2.79.3  3
180.2.79.4  3