【问题标题】:MySQL count consecutive days / days in a rowMySQL连续数天/连续天数
【发布时间】:2020-08-07 11:11:11
【问题描述】:

我有两张桌子。两者都包含一个用户的条目,一个来自早上,一个来自晚上。

我试图获取天数,因此用户在早上和晚上添加了一个条目。每当有一个缺失时,“连胜”就会结束——在当天结束。所以它不介意用户是否在一年前活跃并且连续100天,它只是“用户连续多少天在morningTable和eveningTable中添加条目,返回从今天开始”。

两个表都有每个条目的时间戳。

一个例子:

morningTable:

ID text timestamp
1  bla  2020-08-07 10:30:00
2  blub 2020-08-06 11:30:00
3  abc  2020-08-05 09:20:00
4  def  2020-08-04 06:13:00
5  ghi  2020-08-02 07:05:00

eveningTable:

ID text timestamp
1  abc  2020-08-07 19:30:00
2  def  2020-08-06 20:45:00
3  ghi  2020-08-05 21:30:00
4  jkl  2020-08-03 20:13:00
5  mno  2020-08-01 17:33:00
6  pqr  2020-07-29 19:19:19

因此,对于这两个表,结果必须是数字 3,因为两个表都连续 05.08 - 07.08...

希望你能理解我的情况。

我找到了带有 ROWNUMBER 的示例来查找差距等,但是,很难将它与两个表结合起来。

谁能帮我看看如何用 MySQL 做到这一点?

虽然我也只是在 PHP 中收集所有数据并在那里执行,但是,当数据增长时,使用 MySQL(数据库端)执行它会比在 PHP 中执行要好得多。

提前致谢!

【问题讨论】:

  • 如果有 2 个连续的周期,分别为 3 天和 4 天,期望的输出是多少?
  • 精确指定您的 DBMS 及其版本。
  • @Akina 两张桌子都必须有连胜,所以如果一张桌子有 3 连胜,另一张有 4 连胜,那么它将是 3。事情是从当天返回, 检查下一个条目是否来自两个表中的最后一天,如果不是 -> streak = 0,如果是,则 streak=1,然后转到下一个条目,再次检查两个表,如果两个表中的条目都来自当天before -> streak = 2,否则 streak=1,等等。
  • @Akina 我正在使用 mysql Ver 15.1 Distrib 10.1.45-MariaDB,用于 debian-linux-gnu (x86_64),使用 readline 5.2
  • 没有。如果两个表具有相同的连续 3 天周期,而另一个(稍后或更早)又具有一个连续 4 天的周期怎么办?在这种情况下你需要什么?最伟大的(4)?至少(3)?总和(7)?前/后周期长度?

标签: mysql sql database mariadb


【解决方案1】:

使用union all 组合表格并聚合以得到天数both。然后使用 gaps-and-islands 方法:

with me as (
      select dte
      from ((select date(timestamp) as dte, 1 as morning, 0 as evening
             from morning
            ) union all
            (select date(timestamp) as dte, 0 as morning, 1 as evening
             from evening
            )
           ) me
      group by dte
      having sum(morning) > 0 and sum(evening) > 0
     )
select min(dte), max(dte), count(*)
from (select dte, row_number() over (order by dte) as seqnum
      from me
     ) me
group by dte - interval seqnum day
order by count(*) desc
limit 1;

Here 是一个 dbfiddle。

编辑:

其实这不是特别难为这个问题调整变量:

select min(dte), max(dte), count(*)
from (select dte, (@rn := @rn + 1) as seqnum
      from (select dte
            from ((select date(timestamp) as dte, 1 as morning, 0 as evening
                   from morning
                  ) union all
                  (select date(timestamp) as dte, 0 as morning, 1 as evening
                   from evening
                  )
                 ) me
            group by dte
            having sum(morning) > 0 and sum(evening) > 0
            order by dte
           ) d cross join
           (select @rn := 0) params
     ) me
group by dte - interval seqnum day
order by count(*) desc
limit 1;

【讨论】:

  • 刚才试过了。 PHPMyAdmin 已经在with 上抛出了一个错误,看起来……它似乎不知道with.. 还有其他方法吗?
  • 也在另一个程序中尝试过,然后是 PHPMyAdmin 现在确保它没有 PMA 问题,但它不是......
  • @nameless 。 . . (1) 有一个 dbfiddle 说明这是有效的。 (2) 它需要 MySQL 8+ 或 MariaDB 10.20.0——这两者都是多年前推出的。我注意到您在问题中提到了“rownumber”,暗示您使用的是更新的版本。
  • 是的,我说我找到了带有 ROWNUMBER 的解决方案,但我的版本似乎没有它可用。如上面的 cmets 所述,更新到 MariaDB 10.2 似乎不太容易,因为那里是一些依赖项..所以 MariaDB 10.1 的解决方案会很棒
  • @nameless 。 . .实际上,将这个解决方案用于变量并不难,我将其添加到答案和小提琴中。
【解决方案2】:
WITH 
cte1 AS ( SELECT DISTINCT DATE(m.`timestamp`) uni_date                              
          FROM morningTable m
          JOIN eveningTable e ON DATE(m.`timestamp`) = DATE(e.`timestamp`) ),
cte2 AS ( SELECT uni_date, ROW_NUMBER() OVER (ORDER BY uni_date DESC) rn
          FROM cte1 )
SELECT MAX(DATEDIFF( CURRENT_DATE, uni_date ) + 1) output
FROM cte2
WHERE rn = DATEDIFF( CURRENT_DATE, uni_date ) + 1

如果表格包含太多行,那么使用某个日期值限制在cte1 中检查的日期可能是安全的,该日期值在后一个保证间隔之前。

PS。抱歉,这从 MariaDB 10.2.1 开始可用...


在 MariaDB 10.1.45 中,下一个查询必须有效:

SELECT DATEDIFF(CURRENT_DATE, MIN(uni_date)) + 1 output
FROM ( SELECT DISTINCT DATE(m.`timestamp`) uni_date                              
       FROM morningTable m
       JOIN eveningTable e ON DATE(m.`timestamp`) = DATE(e.`timestamp`) ) t1
WHERE NOT EXISTS ( SELECT NULL
                   FROM ( SELECT DISTINCT DATE(m.`timestamp`) uni_date                              
                          FROM morningTable m
                          JOIN eveningTable e ON DATE(m.`timestamp`) = DATE(e.`timestamp`) ) t2
                   WHERE t2.uni_date = t1.uni_date - INTERVAL 1 DAY )
  AND ( SELECT DATE(MAX(m.`timestamp`))
        FROM morningTable m
        JOIN eveningTable e ON DATE(m.`timestamp`) = DATE(e.`timestamp`) ) = CURRENT_DATE

如果今天在至少一个表中没有行,则查询将返回 NULL - 您可以使用 COALESCE for 来获取零。

【讨论】:

  • 不幸的是,“WITH”在我的版本中不起作用...升级似乎很难,刚刚尝试过..还有其他选择吗?
  • 用 10.1 有什么方法可以做到这一点?
  • 谢谢你,也许我最终能够更新,不过目前还没有成功...
  • 尝试了替代代码..有趣的是,它可以在 db fiddle 中工作,但在我的项目中却不行......我得到的结果是 8,它应该是 3......
  • 您有什么想法,为什么会发生这种情况?
猜你喜欢
  • 2022-07-19
  • 2017-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-11
相关资源
最近更新 更多