Подсчитайте количество строк, которые не находятся в пределах 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