检查连续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,这将返回10

这个查询的首当其冲是在第一个子查询中。 让我们仔细看看,这样我们可以更好地理解这是如何工作的:

使用以下示例数据集:

 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):

结果分组依据consec_set列

最后,我们用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 |