Selecting the number of last consecutive days from timestamp (excepting today)

时间:2015-07-31 20:56:12

标签: mysql timestamp

I have a table A_DailyLogins with the columns ID (auto increment), Key (userid) and Date (timestamp). I want a query which would return the number of last consecutive days from those timestamp based on the Key, for example if he has a row for yesterday, one for two days ago and another one for three days ago, but the last one isn't from four days ago, it would return 3, because this is the number of last days the user was logged in.

My attempt was to create a query selecting the last 7 rows of the players ordered by Date DESC (this is what I wanted in the first place, but then I thought that it would be great to have all the last consecutive days), and then I retrieved the query result and compared the dates (converted to year/month/day with functions from that language [Pawn]) and increased the number of consecutive days when a date is before the other one with one day. (but this is extremely slow compared to what I think that can be done directly only with MySQL)

The closest thing I found is this: Check for x consecutive days - given timestamps in database . But it still isn't how I want it to be, it's still pretty different. I tried to modify it, but it is way too hard for me, I don't have that much experience in MySQL.

1 个答案:

答案 0 :(得分:1)

<强>上下文

consecutive login period成为用户全天登录的时间段(在每个时段的A_DailyLogins中都有一个条目),其中consecutive login period之前或之后的A_DailyLogins中没有条目使用相同的用户

number of consecutive daysconsecutive login period

中最高和最低日期之间的差异

consecutive login period的最大日期在(顺序)之后没有登录条目。

consecutive login period的最短日期之前(顺序)没有登录条目..

<强>计划

  
      
  • 使用相同的用户和连续日期将联接A_DailyLogins留给自己,其中右边为空以查找最大值
  •   
  • 找到最小值的类似逻辑
  •   
  • 通过
  • 按适当的顺序计算最小值和最大值的行排序   
  • 加入行号
  • 的最大值和最小值   
  • 过滤最近登录的日期/今天
  •   
  • 计算范围内最大值和最小值之间的date_diff
  •   
  • 将用户加入上面的结果集并合并用户昨天/今天没有consecutive login period结尾的情况
  •   

<强>输入

+----+------+------------+
| ID | Key  | Date       |
+----+------+------------+
| 25 | eric | 2015-12-23 |
| 26 | eric | 2015-12-25 |
| 27 | eric | 2015-12-26 |
| 28 | eric | 2015-12-27 |
| 29 | eric | 2016-01-01 |
| 30 | eric | 2016-01-02 |
| 31 | eric | 2016-01-03 |
| 32 | nusa | 2015-12-27 |
| 33 | nusa | 2015-12-29 |
+----+------+------------+

<强>查询

select all_users.`Key`, 
coalesce(nconsecutive, 0) as nconsecutive
from
(
  select distinct `Key`
  from A_DailyLogins
) all_users
left join
(
  select
  lower_login_bounds.`Key`,
  lower_login_bounds.`Date` as from_login,
  upper_login_bounds.`Date` as to_login,
  1 + datediff(least(upper_login_bounds.`Date`, date_sub(current_date, interval 1 day))
                     , lower_login_bounds.`Date`) as nconsecutive
  from
  (
    select curr_login.`Key`, curr_login.`Date`, @rn1 := @rn1 + 1 as row_number
    from A_DailyLogins curr_login
    left join A_DailyLogins prev_login
    on curr_login.`Key` = prev_login.`Key`
    and prev_login.`Date` = date_add(curr_login.`Date`, interval -1 day)
    cross join ( select @rn1 := 0 ) params
    where prev_login.`Date` is null
    order by curr_login.`Key`, curr_login.`Date`
  ) lower_login_bounds
  inner join
  (
    select curr_login.`Key`, curr_login.`Date`, @rn2 := @rn2 + 1 as row_number
    from A_DailyLogins curr_login
    left join A_DailyLogins next_login
    on curr_login.`Key` = next_login.`Key`
    and next_login.`Date` = date_add(curr_login.`Date`, interval 1 day)
    cross join ( select @rn2 := 0 ) params
    where next_login.`Date` is null
    order by curr_login.`Key`, curr_login.`Date`
  ) upper_login_bounds
  on lower_login_bounds.row_number = upper_login_bounds.row_number
  where upper_login_bounds.`Date` >= date_sub(current_date, interval 1 day)
  and   lower_login_bounds.`Date` < current_date
) last_consecutive
on all_users.`Key` = last_consecutive.`Key`
;

<强>输出

+------+------------------+
| Key  | last_consecutive |
+------+------------------+
| eric |                2 |
| nusa |                0 |
+------+------------------+

在2016-01-03运行时有效

<强> sqlfiddle