【问题标题】:Daily counts with TSQL?每天使用 TSQL 计数?
【发布时间】:2011-12-05 17:22:51
【问题描述】:

我有一个网站,我在 SQL Server 2008 数据库中记录每个点击链接的客户端指标。我已经编写了查询以获取每日总点击次数,但是我想了解用户在给定时间跨度内(即 5 秒内)点击了多少次。

这里的想法是锁定试图抓取内容的传入 IP 地址。如果在 5 秒内检测到超过 5 次“点击”或来自给定 IP 地址的每日点击次数超过某个值,则假定这是一次抓取尝试。

我尝试了以下几种变体:

-- when a user clicked more than 5 times in 5 seconds
SELECT DATEADD(SECOND, DATEDIFF(SECOND, 0, ClickTimeStamp), 0) as ClickTimeStamp, COUNT(UserClickID) as [Count]
FROM UserClicks
WHERE DATEDIFF(SECOND, 0, ClickTimeStamp) = 5
GROUP BY IPAddress, ClickTimeStamp

这个特别返回以下错误:

Msg 535, Level 16, State 0, Line 3 datediff 函数导致 溢出。分隔两个日期/时间的日期部分数 实例太大。尝试使用不太精确的 datediff 日期部分。

所以我想再一次使用 seconds 日期部分,我相信我在正确的轨道上,但不太明白。

帮助表示赞赏。谢谢。

-- 更新--

很好的建议,帮助我认为这种方法是错误的。每次点击都会进行检查。我应该做的是对于给定的时间戳,检查在最后 5 秒内是否记录了来自同一 IP 地址的 5 次点击。所以它就像,计算 > GetDate() - 5 秒的点击次数

尝试以下仍然不能给我一个准确的数字。

SELECT COUNT(*)
FROM UserClicks
WHERE ClickTimeStamp >= GetDate() - DATEADD(SECOND, -5, GetDate())

【问题讨论】:

  • datediff 的问题是它溢出(显然)。您的 startdate 为 0,ClickTimeStamp 为记录的任何日期时间。任何超过大约 68 年的时间间隔与 datepart 作为秒都会有溢出,因为 datediff 返回一个 int。查看更多信息:stackoverflow.com/questions/1275208/…
  • 请求也存在逻辑问题。假设您有一个在上午 10:00:00 点击的 IP,然后在 10:00:01 和 10:00:04 之间点击了 3 倍。然后又是 10:00:07。您的输出是否会包含 (1+3) 的计数而忽略 10:00:07 还是会是 1+3+1,因为 10:00:07 在 10:00:04 的 5 秒内?根据响应将确定获得结果的方法。

标签: tsql date-parsing datepart


【解决方案1】:

希望我的语法很好,我只有 oracle 可以测试它。我将假设您有一个名为 user_id 的 ID 列,它对该用户是唯一的(它是 user_click_id 吗?如果可以的话,在这些问题中包含表创建语句很有帮助)

您必须对此进行自我加入。逻辑将采用 userclick 并在 userId = userId 上加入 userclick,并且 clicktimestamp 的差异在 0-5 秒之间。然后从子选择开始计数。

select u1.user_id, u1.clicktimestamp, u2.clicktimestamp
from userclicks uc1
left join user_clicks uc2  
    on u2.userk_id = u1.user_id
    and datediff(second,u1.ClickTimeStamp,u2.ClickTimeStamp) <= 5
    and datediff(second,u1.ClickTimeStamp,u2.ClickTimeStamp) > 0

此选择语句应该为您提供 user_id/clicktimestamp 和 1 行,用于与来自同一用户的该 clicktimestamp 相距 0 到 5 秒之间的每条记录。现在只需计算所有 user_id,u1.clicktimestamp 组合并突出显示 5 或更多的组合。获取上面的查询并将其转换为子选择并从中提取计数:

select u1.user_id, u1.clicktimestamp, count(1)
from 
(select u1.user_id, u1.clicktimestamp
from userclicks uc1
left join user_clicks uc2  
    on u2.userk_id = u1.user_id
    and datediff(second,u1.ClickTimeStamp,u2.ClickTimeStamp) <= 5
    and datediff(second,u1.ClickTimeStamp,u2.ClickTimeStamp) > 0) a
group by u1.user_id, u1.clicktimestamp
having count(1) >= 5

希望我可以在 MS 机器上验证我的语法……那里可能有一些错字,但逻辑应该是好的。

【讨论】:

  • 我查看了这种方法,以获取在 5 秒内点击次数
【解决方案2】:

更新的答案:问题出在第三行

SELECT COUNT(*)
 FROM UserClicks
 WHERE ClickTimeStamp >= GetDate() - DATEADD(SECOND, -5, GetDate()) 

GetDate() - DATEADD(SECOND, -5, GetDate()) 表示“取当前日期时间并减去(当前日期时间减去五秒)”。我不完全确定这会产生什么样的价值,但它不会是你想要的。

您仍然想要某种时间段,例如:

SELECT count(*)
 from UserClicks
 where IPAddress = @IPAddress
  and ClickTimeStamp between getdate() and dateadd(second, -5, getdate())

在那里使用 getdate() 我有点不舒服——如果你有一个特定的日期时间值(精确到秒),你应该使用它。

【讨论】:

  • 嗯,我明白了。我试过这个...点击几个链接,运行查询(使用 -5,而不是 5),给出 0。
  • 必须交换第一个 GETDATE() 和 DATEADD() - 这有效。但是,我可以使用 :: WHERE ClickTimeStamp > dateadd(second, -5, getdate()) :: 使用 ">" 和 BETWEEN 有性能差异吗?
  • BETWEEN 会更慢,因为要检查的内容更多——不过,根据索引和其他因素,人类可能不会注意到额外的几毫秒。我使用 BETWEEN 是因为我不能确定你最终会使用 getdate()。至于两者的顺序,(一如既往)是因为我无法测试我的 SO 代码。
【解决方案3】:

假设日志条目仅针对当前活动输入 - 也就是说,每当插入新行时,记录的时间是针对该时间点的,而不是针对任何先前时间点的 - 那么您只需要查看一段时间内的数据,而不必像现在一样查看“所有数据”。

下一个问题是:您多久进行一次检查?如果您关心每秒点击次数,那么介于“每小时一次”和“每 24 小时一次”之间的值似乎是合理的。

下一步:定义你的间隔。 “5 秒内每个 IPAddress 的所有点击”可以有两种方式:设置窗口(00-04、05-09、10-14 等)或滑动窗口(00-04、01-05、02-06 等)。 ) 可能与 5 秒的窗口无关,但可能与更长的时间段更相关(每“天”的点击次数)。

这样,我会采取的一般方法是:

  • 从您关心的最早时间点开始(1 小时前、24 小时前)
  • 设置“桶”,表示可以识别时间窗口(00:00:00 - 00:00:04、00:00:05 - 00:00:09 等)。这可以作为临时表来完成。
  • 对于所有事件,计算自最早点以来经过的秒数
  • 对于每个存储桶,计算命中该存储桶的事件数,按 IPAddress (inner join on the temp table on seconds between lowValue and highValue) 分组
  • 识别那些超过您的阈值 (having count(*) &gt; X) 的人,并为他们开窗。

【讨论】:

  • 这是否意味着用户必须在定义的时间范围内进行 5 次点击?如果用户点击 00:00:02 , 03, 04, 05,06 会怎样?这不适合一个桶。我认为他需要为此自行加入
  • 这就是“滑动窗口”的想法出现的地方。它更棘手,因为你必须抓住发生的每一秒,并在接下来的 5 秒内检查它。 (可能但很复杂,这就是为什么我没有把它拼出来——桶更容易。)
  • ...然后我阅读了您的帖子,这正是我刚才所谈论的。我会给它+1,但我们都没有解决他的问题......
  • 啊,是的,范围只需在 5 秒内增加 1 秒。有道理。
猜你喜欢
  • 1970-01-01
  • 2020-07-22
  • 2020-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-04
  • 1970-01-01
相关资源
最近更新 更多