我想知道是否有人可以帮助我使用一些SQL来返回在两天或更多天内登录数据库表的唯一用户数量(让我们使用7天作为参考)。
我的日志表包含每行中的时间戳(ts)和user_id,表示当时该用户的活动。
以下查询从此日志返回Daily Active Users或DAU:
SELECT FLOOR(ts / 86400) AS day, COUNT(DISTINCT user_id) AS dau
FROM log
GROUP BY day ORDER BY day ASC
现在假设我想添加这个单一查询(或者至少以最有效的方式检索)每周活动用户,或者记录为期7天的唯一用户。但是,我不想在非重叠的几周内分配时间。我需要的是每天计算当天和前6天看到的不同user_ids。
例如:
day users wau
1 1,2 2
4 1,3 3
7 3,4,5 5
8 5 4 (user_id 2 lost from count)
15 2 2 (user_ids 1,3,4 lost from count)
感谢您提供的任何帮助,如果您需要进一步说明,请随时通过评论提出。
答案 0 :(得分:5)
要获得“每周平均用户数”(根据我对您的规范的理解......“每天,在当天和之前的六天内看到的不同user_id的数量”),查询可以使用下面的那个。 (该查询还返回“每日平均用户”计数。
SELECT d.day
, COUNT(DISTINCT u.user_id) AS wau
, COUNT(DISTINCT IF(u.day=d.day,u.user_id,NULL)) AS dau
FROM ( SELECT FLOOR(k.ts/86400) AS `day`
FROM `log` k
GROUP BY `day`
) d
JOIN ( SELECT FLOOR(l.ts/86400) AS `day`
, l.user_id
FROM `log` l
GROUP BY `day`, l.user_id
) u
ON u.day <= d.day
AND u.day > d.day - 7
GROUP BY d.day
ORDER BY d.day
(我还没有对此进行测试;但我会稍后,如果需要更正,我会更新此声明。)
此查询将加入给定日期的用户列表(从u
rowsource)到日志表中的一组天数(d
rowsource)。注意出现在连接谓词(ON子句)中的文字“7”,这就是让用户列表与之前6天“匹配”的原因。
请注意,这也可以扩展为在过去3天内获取不同的用户数,例如,在SELECT列表中添加另一个表达式。
, COUNT(DISTINCT IF(u.day<=d.day AND u.day>d.day-3,u.user_id,NULL)) AS 3day
可以增加字面数“7”以获得更大的范围。上面表达式中的字面值3可以更改为任意天数...我们只需要确保我们已经有足够的前一行(来自d
)加入到{{1 }}
性能注意:由于内联视图(或派生表,MySQL调用它们),此查询可能不会非常快,因为这些内联视图的结果集必须实现为中间MyISAM表。
作为u
别名的内联视图可能不是最佳的;直接加入日志表可能会更快。我在考虑获取给定日期的唯一用户列表,这就是内联视图中的查询对我的影响。我只是更容易概念化正在发生的事情。而且我在想如果你今天输入了数百个相同的用户,那么在我们连接到其他日子之前,内联视图会清除掉一大堆重复项。
限制我们返回的天数的WHERE子句最好添加到u
和u
内联视图中。 (d
内联视图需要在6天前额外添加。)
另一方面,如果ts列是TIMESTAMP数据类型,我更倾向于使用d
表达式来提取日期部分。但是这将返回结果集中的DATE数据类型,而不是整数,这将与您指定的结果集不同。)
DATE(ts)
答案 1 :(得分:2)
这是另一个很好的例子,说明为什么应该使用date,datetime或timestamp字段类型来表示数据库中的时间值而不是unix时间戳。总是有人想要实际查询该字段,然后您不得不进行一堆时间戳转换,因为整数时间戳值没有固有的时间段概念,您需要根据时间段进行查询。在此过程中,您将无法使用字段上的索引。
无论如何,这是一个非常复杂的查询。可能有一种比我建议的更好的方式,但希望我建议至少是有道理的。在这种方法中,您可以通过将表连接到自身来执行笛卡尔连接。然后,使用ON
条件限制记录数,以确保第二个日志表中的日期在第一个日志表中的日期的七天内。最后,进行聚合和分组。查询可能如下所示:
SELECT DATE(FROM_UNIXTIME(log1.ts)) as `day`, COUNT(DISTINCT log2.user_id) as `dau`
FROM log AS log1
INNER JOIN log AS log2
ON DATE(FROM_UNIXTIME(log2.ts)) <= DATE(FROM_UNIXTIME(log1.ts))
AND DATE(FROM_UNIXTIME(log2.ts)) >= DATE_SUB(DATE(FROM_UNIXTIME(log1.ts)), INTERVAL 7 DAY)
GROUP BY `day`
ORDER BY `day` ASC
虽然有警告。如果您有任何相当多的日志条目,则此查询将花费很长时间才能运行,因为您要将结果集中的记录数乘以某个因子,并且您将不使用索引。
您最好的选择可能是在表格中实际创建新的日期格式列并运行更新以填充值。确保您在该字段上有索引。然后您的查询可能如下所示:
SELECT log1.date_field as `day`, COUNT(DISTINCT log2.date_field) as `dau`
FROM log AS log1
INNER JOIN log AS log2
ON log2.date_field <= log1.date_field
AND log2.date_field >= DATE_SUB(log1.date_field, INTERVAL 7 DAY)
GROUP BY `day`
ORDER BY `day` ASC
然后,您可以在此前的所有日志条目中填充此字段。
答案 2 :(得分:0)
这样可以简单直接地获得整周活跃的用户:
选择yearweek(ts)为yearwk,user_id, count(user_id)为每周活动用户 来自日志 分组1,2 有计数(user_id)= 7;