计算彼此不在10秒内的行数

时间:2011-04-01 11:27:16

标签: mysql sql

我跟踪网络访问者。我存储了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 个答案:

答案 0 :(得分:15)

让我先从这张桌子开始。我会使用普通的时间戳,这样我们就可以很容易地看到发生了什么。

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而言为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

答案 1 :(得分:6)

您可以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 :(得分:2)

最简单的方法是将时间戳除以10,并计算这些值和ip_address值的不同组合。这样每个10秒的时间段分开计算。

如果你在样本数据上运行它,它会给你4条曲目,这就是你想要的。

尝试一下,看看它是否能为您提供完整数据集所需的结果:

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

答案 3 :(得分:0)

使用相同的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

答案 4 :(得分:0)

与SQL一样,有许多解决方案可以解决您的问题。 我会使用以下查询,这很简单,应该“足够好”:

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

子查询以10秒的间隔对单个用户的访问进行分组,以便将它们计为一次访问。

原因是可以找到两次访问将出现在不同的10s窗口中的情况,即使这次访问之间的间隔小于10秒。它需要更复杂的逻辑来消除这种情况,并且这种增加的复杂性的分析值将是可疑的(无论如何,10s间隔听起来像一个任意值)。

答案 5 :(得分:0)

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秒作为新访问的任何访问。

答案 6 :(得分:0)

如果在前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)

上的索引或主键

答案 7 :(得分:0)

对于咯咯笑声来说,这是一个UPDATE hack,可以完成你需要的东西。有许多理由不实现这一点,包括但不限于它可能在某一天停止工作的事实。无论如何,假设您的表最初按ip排序 - >时间戳,这应该(通常)给你正确的答案。同样,这是为了完整性,如果您实现此功能,请查看up the risks beforehand

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