SQL:计算一天中的重复用户百分比

时间:2014-12-02 02:31:21

标签: sql postgresql

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

我想知道的是events表中每天重复输入日期的用户百分比。

因此,如果某天d1user_id[a,b,c],而日d2user_id[b,d,e],那么{{1} }是三个中唯一的重复b(来自user_id),d1d1之间有33%的重叠。

我希望能够延长这个任意天数。

有问题的架构:

d2

这是一张大表,每天约有25MM行,每天约有4.5MM CREATE TABLE events ( events_id serial PRIMARY KEY , user_id VARCHAR(255) NOT NULL , created_at datetime NOT NULL ); 秒。

示例数据集:

+---------+---------------------+
| user_id |     created_at      |
+---------+---------------------+
| bob     | 2014-12-02 11:11:11 |
| sally   | 2014-12-02 12:12:11 |
| zed     | 2014-12-02 12:22:11 |
|         | ...                 |
| chris   | 2014-12-03 11:13:11 |
| mark    | 2014-12-03 11:11:13 |
| zed     | 2014-12-03 11:11:33 |
|         | ...                 |
| sydney  | 2014-12-04 11:14:11 |
| zed     | 2014-12-04 11:44:11 |
| chris   | 2014-12-04 11:44:11 |
|         | ...                 |
| sydney  | 2014-12-05 11:15:11 |
| zed     | 2014-12-05 11:55:11 |
| chris   | 2014-12-05 11:55:15 |
| sandy   | 2014-12-05 11:55:51 |
| sydney  | 2014-12-05 11:55:55 |
+---------+---------------------+

预期产出:

+------------+---------------------------+
|    day     | returning_user_percentage |
+------------+---------------------------+
| 2014-12-02 | NULL                      |
| 2014-12-03 | 33                        |
| 2014-12-04 | 66                        |
| 2014-12-05 | 75                        |
+------------+---------------------------+

另外,很可能更简单,第2部分:我想知道每天有多少新用户,其中“新”意味着user_id以前没有见过。

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

回答更新的问题:

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

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(不是" datetime")。为了让唯一身份用户每天,我会转到date并在CTE u中对用户进行分组。这是至关重要,或者您在前一天与重复用户交叉加入重复用户时会得到无意义的结果。

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

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

  • 请注意" day"也由时区定义 - 如果您应该有来自多个时区的数据。只要您存储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
);

这也有助于加快一些或其他查询。