要获得每周的“每周平均用户数”(根据我对您的规范的理解......),每天看到的不同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
行源)的一组日期。请注意出现在连接谓词中的文字“7”( ON条款),这是什么让用户列表“匹配”到前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
)加入到u
的每行中即可。
性能注意:由于内联视图(或派生表,如MySQL调用它们),此查询可能不是很快,因为这些内联视图的结果集必须实现为中间MyISAM表。
别名为u
的内联视图可能不是最优的;直接加入日志表可能会更快。我正在考虑在特定的一天中获取用户的唯一列表,这是内联视图中的查询为我提供的内容。对我来说,构想发生的事情更容易。我在想,如果你有几百个用户输入了一天,内联视图会在我们加入其他日子之前清除一大堆重复项。 在u
和d
内嵌视图中,最好将WHERE子句限制我们返回的天数。(该d
联视图将需要包括额外的较早6天。)
在另一方面,如果TS列timestamp数据类型,我会更倾向于使用一个DATE(ts)
表达式提取的日期部分。但是,这将在结果集中返回一个日期数据类型,而不是一个整数,这将是从你指定的结果集不同。)
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 DATE(k.ts) AS `day`
FROM `log` k
GROUP BY `day`
) d
JOIN (SELECT DATE(l.ts) AS `day`
, l.user_id
FROM `log` l
GROUP BY `day`, l.user_id
) u
ON u.day <= d.day
AND u.day > DATE_ADD(d.day, INTERVAL -7 DAY)
GROUP BY d.day
ORDER BY d.day
谢谢你提供一个非常完整的答案。 ts是一个bigint。第一个查询完美无缺,无论效率如何(目前效率足够高)。 – Protected