检查连续x天 - 给定数据库中的时间戳

Chr*_*ris 16 mysql sql database gaps-and-islands

任何人都可以给我一个想法或暗示如何在数据库表(MySQL)中连续X天检查登录(用户ID,时间戳)的存储情况?

Stackoverflow可以做到这一点(例如像Enthusiast这样的徽章 - 如果你连续登录30天左右......).你必须使用什么功能或者如何做到这一点的想法是什么?

有点像SELECT 1 FROM login_dates WHERE ...

Zan*_*ien 30

您可以使用移位的自外连接和变量来完成此操作.看到这个解决方案

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
Run Code Online (Sandbox Code Playgroud)

这将返回一个1或一个0基于用户是否已经登录在连续30天以上ANYTIME过去.

这个查询首当其冲的是第一个子选择.让我们仔细看看,以便我们更好地了解其工作原理:

使用以下示例数据集:

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');
Run Code Online (Sandbox Code Playgroud)

这个查询:

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
Run Code Online (Sandbox Code Playgroud)

会产生:

示例结果

正如您所看到的,我们正在做的是连接表移动 +1天.对于与前一天不连续的每一天,NULLLEFT JOIN生成一个值.

现在,我们知道其中非连续的日子中,我们可以使用一个变量来区分各通过检测转移表的行是否是连续数日NULL.如果是NULL,则天数不连续,所以只需增加变量即可.如果是NOT NULL,则不要增加变量:

突出显示分组的结果

在我们用递增变量区分每组连续日之后,只需要按每个"集合"(如consec_set列中的定义)进行分组并使用HAVING过滤掉任何小于指定连续日的集合(在你的例子中为30):

通过consec_set列进行分组的结果

最后,我们将THAT查询包装起来,并简单地计算连续30天或更多天的集合数.如果有一个或多个这些集合,则返回1,否则返回0.


请参阅SQLFiddle分步演示

  • 这是本网站上最好的答案之一.如果可以的话,我给它+10. (2认同)

dan*_*era 5

如果此日期范围内的不同(日期)为== X,您可以将X添加到时间戳日期和chech:

这30天中至少每天一次:

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
Run Code Online (Sandbox Code Playgroud)

(你不关心性能要求...;))

*已编辑*仅查询过去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
Run Code Online (Sandbox Code Playgroud)