【问题标题】:How to aggregate data by a dynamic time interval如何按动态时间间隔聚合数据
【发布时间】:2015-10-14 21:05:30
【问题描述】:

使用 psql 我想计算每个唯一访问者。

唯一身份访问者是指在不到一小时之前访问过的访问者。

因此,对于以下几行用户和时间戳,我们将获得 4 个唯一身份访问者的总数,其中 user1 和 user2 分别计为 2。

请注意,我不想在 24 小时内按小时汇总。我想在用户首次访问的时间戳后一小时内进行汇总。

我猜一个直接的 sql 表达式不会这样做。

user1,"2015-07-13 08:28:45.247000" 
user1,"2015-07-13 08:30:17.247000"
user1,"2015-07-13 09:35:00.030000" 
user1,"2015-07-13 09:54:00.652000"
user2,"2015-07-13 08:28:45.247000" 
user2,"2015-07-13 08:30:17.247000"
user2,"2015-07-13 09:35:00.030000" 
user2,"2015-07-13 09:54:00.652000"

所以 user1 在 8:28 到达,这算作一次命中。他在 8 点 30 分回来,这算零。然后他在 9 点 35 分回来,这距离 8 点 30 分还有一个多小时,所以他又受到了打击。然后他在 9:35 回来,距离上次 9:30 仅 5 分钟,所以这算为零。 user1 的总命中数为 2。 user2 也发生了同样的事情,这意味着两次点击,每一次都使其最终达到 4 次。

【问题讨论】:

  • now want to 应该是 not want to 我假设?请始终声明您的 Postgres 版本。你有一个单独的用户表吗?实际的表定义将有助于查看数据类型和约束:CREATE TABLE 脚本或 psql 中\d tbl 的输出。假设用户 60 分钟访问一次呢​​?即使在很长一段时间内也算作 0 次访问?此外,严格来说,您的定义将排除不到一小时前(从现在开始)的访问。
  • 版本是亚马逊上的 Redshift。所以它缺乏某些 postgres 功能。如果用户每 60 分钟访问一次,那么每次访问都会计算一次点击。如果用户在 60 分钟的时间间隔内访问了多次,那么他只会被计算一次。
  • Redshift 不是 Postgres 并且非常有限。我不是粉丝。请记住在问题开始时提供您的版本。

标签: sql postgresql aggregate


【解决方案1】:

这是一种方法:

select count(*)
from t
where not exists (select 1
                  from t t2
                  where t2.user = t.user and
                        t2.timestamp < t.timestamp and
                        t2.timestamp > t.timestamp - interval '1 hour'
                 );

编辑:

如果重复的时间戳是一个潜在问题,您可以使用count(distinct user, timestamp)

注意:usertimestamp 都是关键字,user 是保留的。希望您的实际列被命名为其他名称。

where 子句仅保留前一小时用户没有其他记录的记录。这就是您对“新”用户的定义,因此汇总计数应该是您要查找的内容。

【讨论】:

  • 谢谢。你能解释一下它在做什么吗?这会很有帮助。
  • 我对定义的表述有误。它改变了定义。我赞成你并改写了定义,但现在答案也不正确。
  • @BrianYeh 。 . .我第一次明白了这个问题。这应该得到你想要的计数。
【解决方案2】:

除了定义中的极端情况 (see comment),这个查询比我在 Postgres 9.4 上的本地测试中提供的 @Gordon 快很多倍,无论有没有索引:

SELECT count(ct)
FROM  (
   SELECT user_id, CASE WHEN lead(created_at, 1, 'infinity' )
                      OVER (PARTITION BY user_id ORDER BY created_at)
                 > created_at + interval '1h' THEN true ELSE NULL END AS ct
   FROM   tbl
   ) sub;

核心功能是窗口函数lead() 为当前用户查找“下一个”行。如果下一行距离超过一个小时,则此行计算在内。

lead() 具有经常被忽视的功能,可以为缺失的行提供默认值。填写'infinity' 覆盖角盒。

此查询适用于 timestampcreated_at 中的 NULL 值(未计数)或重复值(仅 1 行计数)。

获得最佳性能的关键是多列索引

CREATE INDEX tbl(user_id, created_at);

对于“唯一访问者”的防弹定义,不同的查询样式可能更好。

【讨论】:

  • 如果 user1 在 9:00 访问,然后在 9:30 访问,然后在 11:00 访问,则这应计为 2 次点击。我对定义的表述很糟糕。我会支持你们两个并编辑定义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-18
相关资源
最近更新 更多