在准备面试时,我遇到了一个 SQL 问题,我希望获得一些关于如何更好地回答它的见解。
Given timestamps, userid, how to determine the number of users who are active everyday in a week?
没什么,但这就是摆在我面前的问题。
最佳答案
我将根据对我来说最有意义的内容以及如果问题与此处相同时我会回复的方式来展示这样的想法:
首先,让我们假设一个数据集,我们将表命名为logins
:
+---------+---------------------+
| user_id | login_timestamp |
+---------+---------------------+
| 1 | 2015-09-29 14:05:05 |
| 2 | 2015-09-29 14:05:08 |
| 1 | 2015-09-29 14:05:12 |
| 4 | 2015-09-22 14:05:18 |
| ... | ... |
+---------+---------------------+
可能还有其他列,但我们不介意。
首先我们应该确定那一周的边界,为此我们可以使用ADDDATE()
。结合今天的日期 - 今天的星期几(MySQL 的 DAYOFWEEK()
)的想法,是星期日的日期。
例如:如果今天是 10 号星期三,Wed - 3 = Sun
,因此 10 - 3 = 7
,我们可以预期星期日是 7 号。
我们可以通过这种方式获取WeekStart
和WeekEnd
时间戳:
SELECT
DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 1-DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 00:00:00") WeekStart,
DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 7-DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 23:59:59") WeekEnd;
注意:在 PostgreSQL 中有一个 DATE_TRUNC()
函数,它返回指定时间单位的开始,给定日期,例如周开始、月份、小时等。但这在 MySQL 中不可用。
接下来,让我们利用 WeekStart 和 weekEnd 来对我们的数据集进行 clice,在此示例中,我将仅展示如何使用硬编码日期进行过滤:
SELECT *
FROM `logins`
WHERE login_timestamp BETWEEN '2015-09-29 14:05:07' AND '2015-09-29 14:05:13'
这应该返回我们切片的数据集,只有相关结果:
+---------+---------------------+
| user_id | login_timestamp |
+---------+---------------------+
| 2 | 2015-09-29 14:05:08 |
| 1 | 2015-09-29 14:05:12 |
+---------+---------------------+
然后我们可以将结果集减少到只有 user_id
,并过滤掉重复项。然后这样数:
SELECT COUNT(DISTINCT user_id)
FROM `logins`
WHERE login_timestamp BETWEEN '2015-09-29 14:05:07' AND '2015-09-29 14:05:13'
DISTINCT
将过滤掉重复项,count 将只返回数量。
结合起来,这就变成了:
SELECT COUNT(DISTINCT user_id)
FROM `logins`
WHERE login_timestamp
BETWEEN DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 1- DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 00:00:00")
AND DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 7- DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 23:59:59")
将 CURDATE()
替换为任何时间戳,以获得该周的用户登录计数。
但我需要将其分解为几天,我听到你在哭泣。当然!这就是:
首先,让我们将提供过多信息的时间戳转换为日期数据。我们添加 DISTINCT
是因为我们不介意同一用户在同一天登录两次。我们计算用户数,而不是登录数,对吧? (注意我们退后一步):
SELECT DISTINCT user_id, DATE_FORMAT(login_timestamp, "%Y-%m-%d")
FROM `logins`
这会产生:
+---------+-----------------+
| user_id | login_timestamp |
+---------+-----------------+
| 1 | 2015-09-29 |
| 2 | 2015-09-29 |
| 4 | 2015-09-22 |
| ... | ... |
+---------+-----------------+
这个查询,我们将用第二个包裹起来,以便计算每个日期的出现次数:
SELECT `login_timestamp`, count(*) AS 'count'
FROM (SELECT DISTINCT user_id, DATE_FORMAT(login_timestamp, "%Y-%m-%d") AS `login_timestamp` FROM `logins`) `loginsMod`
GROUP BY `login_timestamp`
我们使用计数和分组来按日期获取列表,返回:
+-----------------+-------+
| login_timestamp | count |
+-----------------+-------+
| 2015-09-29 | 1 +
| 2015-09-22 | 2 +
+-----------------+-------+
经过所有的努力,两者结合起来:
SELECT `login_timestamp`, COUNT(*)
FROM (
SELECT DISTINCT user_id, DATE_FORMAT(login_timestamp, "%Y-%m-%d") AS `login_timestamp`
FROM `logins`
WHERE login_timestamp BETWEEN DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 1- DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 00:00:00") AND DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 7- DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 23:59:59")) `loginsMod`
GROUP BY `login_timestamp`;
将为您提供本周每天的每日登录明细。同样,替换 CURDATE()
以获得不同的一周。
至于登录的用户本身,让我们以不同的顺序组合相同的东西:
SELECT `user_id`
FROM (
SELECT `user_id`, COUNT(*) AS `login_count`
FROM (
SELECT DISTINCT `user_id`, DATE_FORMAT(`login_timestamp`, "%Y-%m-%d")
FROM `logins`) `logins`
GROUP BY `user_id`) `logincounts`
WHERE `login_count` > 6
我有两个内部查询,第一个是logins
:
SELECT DISTINCT `user_id`, DATE_FORMAT(`login_timestamp`, "%Y-%m-%d")
FROM `logins`
将提供用户列表,以及他们登录的日期,不重复。
然后我们有logincounts
:
SELECT `user_id`, COUNT(*) AS `login_count`
FROM `logins` -- See previous subquery.
GROUP BY `user_id`) `logincounts`
将返回相同的列表,其中包含每个用户的登录次数。
最后:
选择 user_id
FROM logincounts
-- 查看前面的子查询。
WHERE login_count
> 6
过滤掉 7 次未登录的用户,并删除日期列。
这有点长,但我认为它充满了想法,而且我认为它绝对有助于在工作面试中以有趣的方式回答问题。 :)
关于mysql - 在 MySQL 中使用登录时间戳统计活跃用户,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32840887/