【问题标题】:SQL: Calculating the day over day repeat user percentageSQL:计算每日重复用户百分比
【发布时间】:2014-12-03 07:48:48
【问题描述】:

我有一个events 表,其中有一个user_idcreated_at 列。

我想计算的是在events 表中具有每日重复条目的用户百分比。

所以,如果 d1 日有 user_ids [a,b,c]d2 日有 user_ids [b,d,e],那么 b 是三个中唯一重复的 user_id(来自 @ 987654333@),d1d2 之间有 33% 的重叠。

我希望能够将其延长任意天数。

有问题的架构:

CREATE TABLE events (
  events_id serial PRIMARY KEY
, user_id VARCHAR(255) NOT NULL
, created_at datetime NOT NULL
);

这是一个大表,每天有 ~25MM 行,每天有 ~4.5MM 不同 user_ids。

示例数据集:

+---------+----------+ |用户 ID | created_at | +---------+----------+ |鲍勃 | 2014-12-02 11:11:11 | |莎莉 | 2014-12-02 12:12:11 | |泽 | 2014-12-02 12:22:11 | | | ... | |克里斯 | 2014-12-03 11:13:11 | |标记 | 2014-12-03 11:11:13 | |泽 | 2014-12-03 11:11:33 | | | ... | |悉尼 | 2014-12-04 11:14:11 | |泽 | 2014-12-04 11:44:11 | |克里斯 | 2014-12-04 11:44:11 | | | ... | |悉尼 | 2014-12-05 11:15:11 | |泽 | 2014-12-05 11:55:11 | |克里斯 | 2014-12-05 11:55:15 | |沙质 | 2014-12-05 11:55:51 | |悉尼 | 2014-12-05 11:55:55 | +---------+----------+

预期输出:

+------------+---------------+ |天 |返回用户百分比 | +------------+---------------+ | 2014-12-02 |空 | | 2014-12-03 | 33 | | 2014-12-04 | 66 | | 2014-12-05 | 75 | +------------+---------------+

此外,第 2 部分可能更简单:我想知道每天有多少新用户,其中“新”表示以前从未见过 user_id

【问题讨论】:

  • 这个问题并不完全清楚,无论哪种方式,都必须知道用户每天是否可以有多个事件。 表定义会澄清这一点(除其他外)。
  • 更新了更详细的问题。谢谢。
  • (1) 我相信这就是我的回答; (2)你应该在一个问题中坚持一个问题。如果您想询问新客户,请提出另一个问题。
  • 那么同一用户一天可以有任意数量的条目?您如何准确计算最后一行中的 60 %100 % 12-04 的用户出现在 12-05,75 % 的用户 12-05 已经出现在 12-04 和 80 % 条目 与前一天出现的用户有关。无论哪种方式,对我来说都没有加起来。
  • @ErwinBrandstetter,很好,我很抱歉:75% 是正确的,我已经进行了相应的更新。 12-04 的 4 个 user_id 中有 3 个出现在 12-05。是的:同一用户一天可以有多个条目。

标签: sql postgresql


【解决方案1】:

假设created_at 是一个没有时间成分的日期:

select e.created_at,
       avg(case when eprev.user_id is not null then 1.0 else 0.0 end) as overlap
from events e left join
     events eprev
     on e.created_at = eprev.created_at + interval '1' day and e.user_id = eprev.user_id
group by e.created_at

【讨论】:

  • 这不计算“每天重复条目的用户百分比”,而是每天“每天重复条目”的百分比 - 这是完全不同的.很可能 OP 真的想要那个(他提到的重叠点就是这样),但他的说法不同。
  • 感谢您的回复。请参阅上面的更新问题。
  • 如果您想要从 0 到 100 的值,请将 1.0 更改为 100.0
【解决方案2】:

更新问题的答案:

“每天计算前一天有条目的不同用户的百分比。”

WITH e AS (SELECT created_at::date, user_id AS day FROM events GROUP BY 1, 2)
SELECT e.day
     , round(100.0 * count(e1.user_id) / count(*), 2) AS pct_repeat_user
FROM   e
LEFT   JOIN e e1 ON e1.user_id = e.user_id
                AND e1.day = e.day - 1
GROUP  BY 1
ORDER  BY 1;

返回您想要的结果 - 但第一天是 0 而不是 NULL,这对我来说似乎更正确。

解释

  • 您的“日期”实际上是timestamp(不是“日期时间”)。为了获得每天的唯一用户,我投给了date 并在 CTE 中对用户进行了分组u。这至关重要,否则您会因为在一天交叉加入重复用户和前一天重复用户而得到荒谬的结果。

  • 在此基础上,LEFT JOIN 到前一天。计算“昨天”可以找到的用户数,然后除以“今天”的用户数。在除法之前乘以100.0 将数字强制转换为numeric,并使用round() 获得所需的小数位数。

  • 由于day 是数据类型date,现在您只需从中减去integer 1 即可得到“昨天”。

  • 请注意,“天”也由时区定义 - 如果您应该拥有来自多个时区的数据。只要您只存储timestamp 就不会起作用,但您的数据中可能存在固有错误。详情:

SQL Fiddle.

数据库设计

对于每个用户的数百万行和多个条目,我强烈建议创建一个单独的 users 表并引用它以减少磁盘空间并提高性能:

CREATE TABLE users (
  user_id serial PRIMARY KEY
, username text NOT NULL
);

CREATE TABLE events (
  events_id serial PRIMARY KEY
, user_id int NOT NULL REFERENCES users
, created_at timestamp NOT NULL
);

这也有助于加快数字或其他查询。

【讨论】:

  • 谢谢欧文。在您的第一个查询中,pct_repeat_user 子查询不应该以天为界,否则我将获得表中所有user_ids 的不同计数。不幸的是,在第二个查询中,我没有可以加入的users 表。我仍在处理您的查询,看看它是否给出了我想要的结果。
  • @findchris:考虑一下您已澄清问题的新答案。
  • 这看起来不错@Erwin。谢谢你。我原以为我必须使用更复杂的东西,例如RANKLAG,但您的解决方案又好又简单。
猜你喜欢
  • 1970-01-01
  • 2021-12-17
  • 1970-01-01
  • 1970-01-01
  • 2020-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多