Подсчитать количество строк, которые не находятся в пределах 10 секунд друг от друга - PullRequest
12 голосов
/ 01 апреля 2011

Я отслеживаю посетителей сайта.Я сохраняю 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, я был бы признателен за любую помощь в этом!

Ответы [ 8 ]

15 голосов
/ 07 апреля 2011

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

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>

Временная метка, обозначающая окончание посещения, имеет ноль для собственной страницы next_page. Это связано с тем, что никакая временная метка не меньше или равна временной метке + 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
6 голосов
/ 01 апреля 2011

Вы можете 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

Редактировать

Следующий скрипт работает в 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
2 голосов
/ 01 апреля 2011

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

Если вы запустите это на своих данных примера, это даст вам 4 трека, а это то, что вы хотите, я думаю.

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

SELECT COUNT(DISTINCT ip_address, FLOOR(time_stamp/10)) AS tracks 
FROM tracking
0 голосов
/ 14 апреля 2011

Ради смеха, вот взлом 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
0 голосов
/ 13 апреля 2011

Следующая логика будет считать посещение только «уникальным посещением», если в течение предыдущих 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)

0 голосов
/ 13 апреля 2011
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 секунд, как новое посещение.

0 голосов
/ 08 апреля 2011

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

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

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

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

0 голосов
/ 01 апреля 2011

Создайте левое соединение с записями с тем же 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...