【问题标题】:Selecting the number of last consecutive days from timestamp (excepting today)从时间戳中选择最后连续天数(今天除外)
【发布时间】:2015-10-23 15:46:08
【问题描述】:

我有一个表 A_DailyLogins 与列 ID(自动增量)、Key(用户 ID)和 Date(时间戳)。我想要一个查询,它会根据Key 从这些时间戳返回最后连续天数,例如,如果他昨天有一行,两天前有一行,三天前有另一行,但最后一个不是四天前的,它会返回 3,因为这是用户最后登录的天数。

我的尝试是创建一个查询,选择由Date DESC 排序的最后 7 行球员(这是我一开始想要的,但后来我认为让所有最后连续天),然后我检索了查询结果并比较了日期(使用该语言 [Pawn] 的函数转换为年/月/日)并增加了一个日期在另一个日期之前的连续天数。 (但与我认为只能使用 MySQL 直接完成的操作相比,这非常慢)

我找到的最接近的是:Check for x consecutive days - given timestamps in database。但它仍然不是我想要的样子,它仍然非常不同。我试图修改它,但对我来说太难了,我没有太多的 MySQL 经验。

【问题讨论】:

  • 我不明白你想要什么。我建议你添加一个真实数据的例子。
  • 您是否有可能修改数据库和/或数据的创建方式?
  • @dognose:是的,但我想使用时间戳是最好的方法。无论如何,我可以随意修改它。
  • @m0skit0:我有一个表格,其中包含IDKeyDate 列(时间戳),如果玩家之前没有登录,它会插入一个新行在那一天。我想获取玩家登录的连续天数(但只有当前连续天数,而不是其他天数)。

标签: mysql timestamp


【解决方案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

【讨论】:

  • 哇!我不知道这会这么复杂!这很棒,这正是我想要的。谢谢!
  • 是的,可能有点棘手,因为 mysql 不支持 row_number 等窗口函数,但是一旦您了解了 mysql 变量的工作原理和滞后变量模式,它就足够简单了
  • 一个小问题:你能做到,只有当最后连续的一天(或唯一的一天)是昨天时,它才会计算那些连续的天数?例如,现在我们在 2015 年 1 月 8 日,我希望它仅在最后一天在 2015 年 7 月 31 日时才计算连续天数,而不是在 2015 年 7 月 30 日或更早时计算。如果只有昨天,应该返回1(或者到昨天是连续的多少天),如果最后一个是更早的,应该返回0。
  • @NistorConstantin 添加了小提琴,请检查输出行为
  • @NistorConstantin 已更正 - 问题是第一个用户使用 @lag_user 处理空值。即,当它检查@lag_user Key 是否因为@lag_user 为空时,它返回false,因此将其调整为也允许空。更新答案
猜你喜欢
  • 1970-01-01
  • 2012-03-31
  • 2017-07-10
  • 1970-01-01
  • 1970-01-01
  • 2014-10-25
  • 2020-10-21
  • 1970-01-01
  • 2014-05-25
相关资源
最近更新 更多