检查连续x天 – 给定数据库中的时间戳
任何人都可以给我一个想法或提示如何检查数据库表(MySQL)连续X天login(用户名,时间戳)存储?
Stackoverflow做到了这一点(例如徽章像发烧友 – 如果你连续30天左右login…)。 你需要使用什么function?怎么做?
类似于SELECT 1 FROM login_dates WHERE ...
?
你可以使用一个移位的自外连接和一个variables来完成这个任务。 看这个解决scheme:
SELECT IF(COUNT(1) > 0, 1, 0) AS has_consec FROM ( SELECT * FROM ( SELECT IF(b.login_date IS NULL, @val:=@val+1, @val) AS consec_set FROM tbl a CROSS JOIN (SELECT @val:=0) var_init LEFT JOIN tbl b ON a.user_id = b.user_id AND a.login_date = b.login_date + INTERVAL 1 DAY WHERE a.user_id = 1 ) a GROUP BY a.consec_set HAVING COUNT(1) >= 30 ) a
如果用户在过去的ANYTIME连续30天或更长时间login,这将返回1
或0
。
这个查询的首当其冲是在第一个子查询中。 让我们仔细看看,这样我们可以更好地理解这是如何工作的:
使用以下示例数据集:
CREATE TABLE tbl ( user_id INT, login_date DATE ); INSERT INTO tbl VALUES (1, '2012-04-01'), (2, '2012-04-02'), (1, '2012-04-25'), (2, '2012-04-03'), (1, '2012-05-03'), (2, '2012-04-04'), (1, '2012-05-04'), (2, '2012-05-04'), (1, '2012-05-05'), (2, '2012-05-06'), (1, '2012-05-06'), (2, '2012-05-08'), (1, '2012-05-07'), (2, '2012-05-09'), (1, '2012-05-09'), (2, '2012-05-11'), (1, '2012-05-10'), (2, '2012-05-17'), (1, '2012-05-11'), (2, '2012-05-18'), (1, '2012-05-12'), (2, '2012-05-19'), (1, '2012-05-16'), (2, '2012-05-20'), (1, '2012-05-19'), (2, '2012-05-21'), (1, '2012-05-20'), (2, '2012-05-22'), (1, '2012-05-21'), (2, '2012-05-25'), (1, '2012-05-22'), (2, '2012-05-26'), (1, '2012-05-25'), (2, '2012-05-27'), (2, '2012-05-28'), (2, '2012-05-29'), (2, '2012-05-30'), (2, '2012-05-31'), (2, '2012-06-01'), (2, '2012-06-02');
这个查询:
SELECT a.*, b.*, IF(b.login_date IS NULL, @val:=@val+1, @val) AS consec_set FROM tbl a CROSS JOIN (SELECT @val:=0) var_init LEFT JOIN tbl b ON a.user_id = b.user_id AND a.login_date = b.login_date + INTERVAL 1 DAY WHERE a.user_id = 1
会产生:
正如你所看到的,我们正在做的是将连接的表格移动 +1天。 对于前一天不连续的每一天,由LEFT JOIN生成一个NULL
值。
现在我们知道非连续天的位置,我们可以通过检测移位表的行是否为NULL
来使用variables来区分每个连续日。 如果它们是NULL
,则这些日子不是连续的,所以只需增加variables即可。 如果它们NOT NULL
,那么不要增加variables:
在用递增variables区分连续日子集合之后,就可以简单地按每个“集合”(在consec_set
列中定义)进行HAVING
,并使用HAVING
过滤掉任何小于指定集合的集合连续几天(在你的例子中是30):
最后,我们用THAT查询来计算连续30天或更多天的数量。 如果有一个或多个这些集合,则返回1
,否则返回0
。
看到一个SQLFiddle分步演示
如果此date范围内的不同(date)为== X,则可以将X添加到时间戳date和chech中:
这30天中至less每天一次:
SELECT distinct 1 FROM login_dates l1 inner join login_dates l2 on l1.user = l2.user and l2.timestamp between l1.timestamp and date_add( l1.timestamp, Interval X day ) where l1.user = some_user group by DATE(l1.timestamp) having count( distinct DATE(l1.timestamp) ) = X
(你不需要对性能要求进行分析…))
*编辑*只有最后X天的查询:这30天里每天都有一次查询
SELECT distinct 1 FROM login_dates l1 where l1.user = some_user and l1.timestamp > date_add( CURDATE() , Interval -X day ) group by l1.user having count( distinct DATE(l1.timestamp) ) = X
这是一个很难用SQL解决的问题。
问题的核心是,您需要在一个查询中将dynamic结果集相互比较。 例如,您需要获取一个DATE的所有login/会话ID,然后将它们与列表的JOIN或UNION进行联合,以便从DATE()(您可以使用DATE_ADD确定)对login进行分组。 你可以为N个连续的date做这个。 如果您有剩余的行,那么这段时间已经login了。
假设如下表格:
sessionid int,创builddate
这个查询返回最近两天有所有行的sessionid:
select t1.sessionid from logins t1 join logins t2 on t1.sessionid=t2.sessionid where t1.created = DATE(date_sub(now(), interval 2 day)) AND t2.created = DATE(date_sub(now(), interval 1 day));
正如你所看到的那样,SQL将在30天内变得粗糙。 有一个脚本生成它。 😀
这进一步假设每天login表更新会话。
我不知道这是否真的解决了你的问题,但我希望我帮助解决了这个问题。
祝你好运。
在login_dates表中有一个额外的列连续date是不是更加简单,默认值为1.这将表明当天结束的连续date的长度。
在login_dates上触发后,您可以创build一个插入,您可以检查前一天是否有条目。
如果没有,则该字段将具有默认值1,意味着在该date开始新的序列。
如果这是前一天的条目,那么将days_logged_in值从默认值1更改为比前一天大1。
例如:
| date | consecutive_days | |------------|------------------| | 2013-11-13 | 5 | | 2013-11-14 | 6 | | 2013-11-16 | 1 | | 2013-11-17 | 2 | | 2013-11-18 | 3 |