mysql 连续打卡_MySQL查询连续打卡信息?

2023-11-08

最近多次看到用SQL查询连续打卡信息问题,自己也实践一波。抛开问题本身,也是对MySQL窗口函数和自定义变量用法的一种练习。

136be34314a57ba454d97dfa8ef5d707.png 01 建表 所用数据库为MySQL8.0,简单而不失一般性,建立一个仅有记录id、用户id、日期和打卡标记共4个字段的数据表。建表语句为:

1CREATE TABLE `testd` (

2  `id` int NOT NULL AUTO_INCREMENT,

3  `userid` int NOT NULL,

4  `dday` date DEFAULT(CURRENT_DATE),

5  `flag` tinyint(1) DEFAULT(0),

6  PRIMARY KEY (`id`)

7) ENGINE=InnoDB

为使查询更具一般性,设计数据表中有两个用户、日期存在跨月、且可能存在日期不连续的情形(虽然实际中可能并不存在这样的情况),插入如下数据:

1INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (1, 1, '2020-03-31', 1);

2INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (2, 1, '2020-04-01', 0);

3INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (3, 1, '2020-04-02', 1);

4INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (4, 1, '2020-04-03', 1);

5INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (5, 1, '2020-04-04', 1);

6INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (6, 1, '2020-04-05', 1);

7INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (7, 1, '2020-04-07', 0);

8INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (8, 1, '2020-04-08', 1);

9INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (9, 1, '2020-04-09', 1);

10INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (10, 1, '2020-04-10', 1);

11INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (11, 1, '2020-04-11', 0);

12INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (12, 2, '2020-03-31', 0);

13INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (13, 2, '2020-04-01', 1);

14INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (14, 2, '2020-04-02', 1);

15INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (15, 2, '2020-04-03', 1);

16INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (16, 2, '2020-04-04', 1);

17INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (17, 2, '2020-04-05', 0);

18INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (18, 2, '2020-04-07', 0);

19INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (19, 2, '2020-04-08', 1);

20INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (20, 2, '2020-04-09', 1);

21INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (21, 2, '2020-04-10', 1);

22INSERT INTO `testd`(`id`, `userid`, `dday`, `flag`) VALUES (22, 2, '2020-04-11', 1);

查询目标是获得用户的连续打卡天数,包括最近连续打卡信息、历史最长打卡信息和所有连续打卡信息。 02 分析 对于这种类型的需求,个人认为应该属于滑动窗口问题,即满足同一取值的最大窗口长度(打卡情况的窗口长度),MySQL自从8.0版本开始,提供了常用的窗口函数用法,像之前的3种排名(参考一文解决所有MySQL分类排名问题)问题在8.0中运用窗口函数就非常简单。

136be34314a57ba454d97dfa8ef5d707.png

MySQL8.0支持窗口函数 但对于这个具体需求,似乎现有窗口函数并不能直接得到答案,所以我们考虑退而求其次——采用自定义变量的方法曲线求解。 为简单起见,我们采取先单用户再多用户的思路逐步分析。

02 单用户打卡查询 单用户情况下,求解连续打卡信息意味着,在对日期顺序排序的基础上:

如果用户今天打卡:

如果昨天也打卡,则今天连续打卡天数是在昨天基础上+1

如果昨天未打卡,则连续打卡天数从1开始,计数1

如果未打卡,则记连续打卡天数为0

进一步地,我们发现在在定义用户未打卡时打卡天数=0的基础上,当用户打卡时无论前一天是否打卡,其打卡天数均为前一天+1(即要么是0+1,要么是N+1)

进而,可以写出如下SQL语句:

1SELECT

2    userid, dday, flag, @pre_check := IF(flag, @pre_check + 1, 0) AS 'check_days'

3FROM

4    testd, (SELECT @pre_check := 0 ) init

5WHERE

6    userid = 1

7ORDER BY

8    dday

其中限定userid=1是为了仅考虑单用户情形,自定义变量@pre_check表示前一天打卡天数, init子表用于初始化变量@pre_check=0。得到查询结果:

136be34314a57ba454d97dfa8ef5d707.png

单用户连续打卡信息

得到这个结果,那么对于单用户时求其打卡信息就简单多了,例如求其最近连续打卡天数,则用如下SQL:

1SELECT

2    userid, dday, check_days

3FROM

4    (

5        SELECT

6            userid, dday, flag, @pre_check := IF( flag, @pre_check + 1, 0 ) AS 'check_days'

7        FROM

8            testd, (SELECT @pre_check := 0 ) init

9        WHERE

10            userid = 1

11        ORDER BY

12            dday

13    ) tmp

14WHERE

15    check_days > 0

16ORDER BY

17    dday DESC

18LIMIT 1

实际上就是在前一步得到的衍生表基础上,筛选打卡天数大于0的日期按降序排列,取出最近一条记录即为最近的连续打卡日期。筛选条件改成大于7就是最近的连续7天打卡的日期。得到查询结果:

136be34314a57ba454d97dfa8ef5d707.png 单用户最近连续打卡信息 那么如果要查询历史打卡天数最长的日期呢?只需要按打卡天数降序排列即可:

1SELECT

2    userid, dday, check_days

3FROM

4    (

5        SELECT

6            userid, dday, flag, @pre_check := IF( flag, @pre_check + 1, 0 ) AS 'check_days'

7        FROM

8            testd, (SELECT @pre_check := 0 ) init

9        WHERE

10            userid = 1

11        ORDER BY

12            dday

13    ) tmp

14ORDER BY

15    check_days DESC

16LIMIT 1

得到结果:

136be34314a57ba454d97dfa8ef5d707.png

单用户历史最长打卡信息

03 多用户打卡查询

在单用户打卡查询的基础上,其实多用户打卡查询的思路是一致的,只不过为了区分用户维度,需要再增加一个自定义变量。对用户和日期进行排序,而后采取以下逻辑:

如果当前记录的用户与上一个用户相同:

如果该用户当天打卡,则其打卡天数是前一天打卡天数+1

否则,即当天未打卡,则打卡天数为0

如果当前记录用户是新用户:

如果打卡,则打卡计数为1

否则,计数为0

基于以上思路,可写出基本的SQL语句如下:

1SELECT

2    userid, dday, flag,

3    @pre_check := IF(userid = @pre_userid, IF(flag, @pre_check + 1, 0), IF(flag, 1, 0)) AS 'check_days',

4    @pre_userid := userid AS 'Pre_user'

5FROM

6    testd, (SELECT @pre_check := 0, @pre_userid := null ) init

7ORDER BY

8    userid, dday

其中增加了一个自定义变量@preuser,表示当前行的前一条记录用户信息。得到查询结果:

136be34314a57ba454d97dfa8ef5d707.png

多用户连续打卡信息

在获得各用户连续打卡天数信息后,如果是查询各用户最近连续打卡天数,则可依据用户进行分组后查询该用户最近连续打卡天数大于0的信息(为表述简单,记前面查询到的衍生表为tmp表):

1SELECT

2    userid, dday, check_days

3FROM

4    tmp

5WHERE

6    (userid, dday) in ( SELECT userid, max(dday)

7                        FROM tmp

8                        WHERE check_days>

9                        GROUP BY userid   )

得到查询结果:

136be34314a57ba454d97dfa8ef5d707.png

多用户最近连续打卡信息

类似的,如果要查询各用户的历史最长连续打卡信息,依然采取类似思路,有:

1SELECT

2    userid, dday, check_days

3FROM

4    tmp

5WHERE

6    (userid, check_days) in ( SELECT userid, max(check_days)

7                              FROM tmp

8                              GROUP BY userid )

得到查询结果:

136be34314a57ba454d97dfa8ef5d707.png

多用户历史最长连续打卡信息 其中,注意到用户2有两次历史连续打卡天数为4的记录,且都是该用户最长打卡记录。

04 各用户所有连续打卡信息

以上是查询了各用户1次特定的打卡信息(要么是最近,要么是历史最长),如果要查询各用户所有连续打卡信息呢?例如,某用户在’2020-04-01’至’2020-04-04’连续4天打卡、在’2020-04-06’至’2020-04-10’连续5天打卡,则最终显示的2020-04-04的4天和2020-04-10的6天两条信息。 实际上,在以上查询的基础上,这样的查询就是在多用户连续打卡信息表(03部分第一张结果)中筛选出其后一天打卡为0的记录。也就是说,如果当前记录的下一天仍然是连续打卡,那么当前记录不作为最终结果;如果下一天打卡为0,才是最终想提取的信息。

为了实现这一需求,如果是8.0版本,可直接借助窗口函数lead()进行判断。例如:

1SELECT

2    userid, dday, check_days,

3    lead(flag) over(partition by userid) as 'nxt_flag'

4FROM

5    (SELECT

6        userid, dday, flag,

7        @pre_check := IF(userid = @pre_userid, IF(flag, @pre_check + 1, 0), IF(flag, 1, 0)) AS 'check_days',

8        @pre_userid := userid AS 'Pre_user'

9    FROM

10        testd, (SELECT @pre_check := 0, @pre_userid := null ) init

11    ORDER BY

12        userid, dday )tmp

得到结果:

136be34314a57ba454d97dfa8ef5d707.png

带次日打卡信息的多用户连续打卡记录

基于此衍生表,进一步查出次日当日连续打卡>0且次日打卡为0或者为null的记录(null代表当前是最后一天)即可。其SQL语句:

1SELECT userid, dday, check_days

2FROM

3    (

4        SELECT

5            userid, dday, check_days,

6            lead(flag) over(partition by userid) as 'nxt_flag'

7        FROM

8            (

9                SELECT

10                    userid, dday, flag,

11                    @pre_check := IF(userid = @pre_userid, IF(flag, @pre_check + 1, 0), IF(flag, 1, 0)) AS 'check_days',

12                    @pre_userid := userid AS 'Pre_user'

13                FROM

14                    testd, (SELECT @pre_check := 0, @pre_userid := null ) init

15                ORDER BY

16                    userid, dday

17            ) tmp

18    ) tt

19WHERE check_days and (nxt_flag is null or nxt_flag=0)

最终,得到查询结果:

136be34314a57ba454d97dfa8ef5d707.png

各用户连续打卡记录 当然,如果是MySQL8.0以下版本,是没有lead()窗口函数可以直接调用的,次此时可借助连接查询或者子查询,设定连接条件是表1和表2用户相同、日期相差为1即可。

05 总结

本文对MySQL中查询用户连续打卡这一问题进行了分析,主要是基于自定义变量的方式,实现了以下问题:

查询各用户每天的连续打卡信息(包括未打卡时记为0)

查询各用户最近连续打卡信息

查询各用户历史最长打卡信息

查询各用户所有打卡记录信息

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

mysql 连续打卡_MySQL查询连续打卡信息? 的相关文章

  • 三大排序算法

    目录 大O和推导过程 冒泡排序 冒泡排序的思路 冒泡排序的实现 冒泡排序的效率 O N 选择排序 选择排序的思路 选择排序实现 选择排序的效率 O N 插入排序 插入排序的思路 插入排序的实现 插入排序的效率 O N 大O和推导过程 公司规
  • 2023美赛C题:预测Wordle结果-思路详解及参考代码

    一 题目解析 总体来看与去年的C题比较相似 唯一一道有数据 不需要自己额外找 的题目 选题人估计也最多 本质是数据分析题目 需要建立预测模型 分类模型 特征挖掘等 相对来说出思路比较简单 想出彩比较难 所以在分析建模时一定要多维度思考 不然
  • spring三级缓存解决循环依赖

    一 循环依赖 简单说就是 A类中有B属性 B类中有A属性 创建A对象时发现有B属性 就开始创建B对象 此时时又发现B对象中有A属性 又要创建A对象 产生循环依赖现象 示例图 二 Spring解决循环依赖 使用缓存解决循环依赖的流程图 spr
  • 计算机高级应用大赛,理工学院成功开展第三届计算机应用技能竞赛之高级OFFCIE应用大赛...

    为提升学生计算机办公软件应用技能 进一步激发学生的学习兴趣 11月21日下午 理工学院第三届计算机应用技能竞赛之高级OFFCIE应用大赛在A5401 A5403 A5411三个机房成功举行 323名学生报名参加此次竞赛 竞赛分为Word E
  • 嵌入式Linux(五)—嵌入式C语言(运算符2)

    目录 逻辑结构 类型修饰符 auto register 补充 内存和寄存器的关系 Static 静态 Extern 外部申明 Const Volatile 运算符 算数运算操作 逻辑运算 或 与 位运算 移位 赋值运算 内存访问 逻辑结构
  • Windows 网络编程

    Winsock是Windows下网络编程的规范 该规范是Windows下得到广泛应用的 开放的 支持多种协议的网络编程接口 在MFC中MS为套接口 提供了相应的类CAsyncSocket和CSocket CAsyncSocket提供基于异步
  • securecrt破解版64位

    securecrt 破解版是一款支持SSH1和SSH2的终端仿真程序 这个程序能够在windows系统中登陆UNIX或Linux的服务器主机并且还能进行管理设置 是一款非常强大的ssh传输软件 是用于连接运行包括Windows UNIX和V
  • 激发新动能 多地发力数字经济

    发力数字经济 地方正紧锣密鼓展开新一轮规划部署 近期 云南 陕西 江苏 江西等多地出台相关举措 明确未来几年数字经济核心产业发展目标 并进一步完善资金 人才等配套政策 相关专家表示 发展数字经济是把握新一轮科技革命和产业变革新机遇的战略选择
  • 学习日记——时钟温湿计_Demo

    程序例程 如果成功接入则进入SNTP初始化 如果连接时候wifi错误或者是密码错误进入微信智能配网 以上步骤和微信智能配网相同 增加了SNTP初始化这一步 配网成功也执行SNTP初始化 SNTP初始化执行完毕之后每隔一秒种获取网络时间 并且
  • 【解决】控制台解析preview和response数据不一致,并使用transformResponse修改响应数据

    问题 控制台解析preview和response数据不一致 比如 id 1246000001606460673 会被默认解析成 id 1246000001606460700 在Preview 预览功能 中 控制台会把发送过来的json数据自
  • C++ 性能优化篇四《优化字符串的使用:案例研究》

    只有少数人才能触摸到魔法琴弦 string 可是聒噪的名声却企图击败他们 悲哀于那些从来都不歌唱的人们 死亡时却要带着他们的音乐陪葬 奥利弗 温德尔 霍姆斯 1 无声 1858 C 的 std string 类模板是 C 标 准 库 中 使
  • 转贴:华为 SmartAX MT800 固件升级(升级为VC100R004C01B010)并开启路由全过程(2004年8月7日更新) [精华]

    http bbs pcshow net cgi bin threaded show cgi tid 350483777 h 1 bpg 1 age 1 请认真的看上面的文章 如果软件版本低的话 要升级 1为升级工具 2为升级的软件包 升级之
  • 正式发布!中国首个LF Edge捐赠项目Baetyl 2.2发布

    Baetyl作为中国首个加入LFEdge基金会的边缘计算项目 自2019年由百度捐赠以来 在开放中立的社区环境中得到不断的支持与发展 在众多活跃的贡献者的努力下 Baetyl实现了更多具有挑战性的功能 正式升级为Baetyl v2 2版本
  • 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数。

    定义栈的数据结构 请在该类型中实现一个能够得到校的最小元素的min函数 在该栈中 调用pop push 及min的时间复杂度都是0 1 param
  • pyqt5 QGraphicsView内缩放显示的图像

    from PyQt5 QtWidgets import QApplication QGraphicsView QGraphicsScene QGraphicsItem QGraphicsPixmapItem from PyQt5 QtCor
  • log4j-slf4j-impl cannot be present with log4j-to-slf4j 之类的问题,解决maven依赖冲突

    如题所示 本文主要用户解决maven的依赖冲突 并提供通用的解决方案 先放错误 SLF4J Class path contains multiple SLF4J bindings SLF4J Found binding in jar fil
  • 【Android】Room新手快速入门

    Room是什么 Room是Google推出的一款android平台上的ORM数据库框架 它类似于GreenDao 但比GreenDao更加简洁高效 是官方推荐使用的数据库框架 引入Gradle依赖 Room api androidx roo
  • 私有仓库修改docker.json报错

    我试了目前网上所有方法都不行自己摸索了很久 提供一种解决方案 找到你的阿里云镜像加速器 重新把配置阿里云镜像的那几行linux代码 再执行一次 然后紧接着马上去 etc docker daemon json加你的私有仓库http配置 再重启
  • NUC980开源项目31-can总线调试

    上面是我的微信和QQ群 欢迎新朋友的加入 以CAN0为例 在内核中配置 文件系统支持 重新烧录 启动 正常挂载 CAN终端测试 查看CAN接口 root myir ls sys class net can0 eth0 lo usb0 wla

随机推荐

  • docker部署fisco bcos区块链浏览器

    首先你要搭建一个myql数据库 理论上mariadb也支持 并创建一个数据库 再建个账号给区块链浏览器使用 如 CREATE DATABASE fisco CHARACTER SET utf8mb4 COLLATE utf8mb4 bin
  • [Excel VBA]如何自动关闭MsgBox?

    本文译至 http itpro nikkeibp co jp atcl column 15 090100207 090700150 VBA的MsgBox函数 直到用户点击按钮前都会继续显示 经过指定时间后自动关闭的MsgBox 可以使用Wi
  • 中高级程序员需求技能

    初级 中级 1 团队精神和协作能力 把它作为基本素质 并不是不重要 恰恰相反 这是程序员应该具备的最基本的 也是最重要的安身立命之本 把高水平程序员说成独行侠的都是在呓语 任何个人的力量都是有限的 即便如linus这样的天才 也需要通过组成
  • Unity发送Post请求

    using System Collections using System Text using UnityEngine using UnityEngine Networking public class PostMsg MonoBehav
  • CSDN竞赛第34期题解

    CSDN竞赛第34期题解 1 题目名称 最优利润值 你在读的经营课程上 老师布置了一道作业 在一家公司的日常运营中 是会对一些商品的价格走势根据一些经验和数据 进行预估 并据此进行决策 例如 假设某商品每天的价格都有可能变动 我们要做的就是
  • 从URL取值传给后端

    从URL传值给后端 http 127 0 0 1 8080 blog content html id 8 点击浏览文章详情 跳转至详情页面 从 url 中拿出文章 id 传给后端 首先拿到url 然后判断是否有值 从问号后面取值 param
  • 【Qt-11】http通信(Get同步收发)

    Qt 9 HTTP请求 post方式 WXG1011的博客 CSDN博客 QT 6 QWebApp服务器搭建及使用 qtwebapp WXG1011的博客 CSDN博客 写在前面 在上面两篇博文的基础上 继续迭代功能 上面两篇博客已实现QW
  • 谈Delphi编程中资源文件的应用

    一 初级应用篇 资源文件一般为扩展名为res的文件 在VC中资源文件用得非常普遍 但Delphi在其联机帮助中对资源文件没作什么介绍 其实利用其自带的资源编译工具BRCC32 EXE 一般位于 Delphi BIN目录下 我们完全可以做出跟
  • StringBuilder和StringBuffer&String的区别,以及它的基本用法

    StringBuilder在java5中引入 算的上是一个StringBuffer的一个用于单线程的版本 StringBuilder用于拼接字符串 用法跟StringBuffer差不多 都是创建一个字符缓存区 不用像String一样每增加一
  • JVM Tenured space is exhausted

    使用Android Studio命令gradlew assembleRelease打包apk 遇到了JVM Tenured space is exhausted的错误 网上查询后原因是JVM分配内存不足 需要在项目下的grade prope
  • 期货和股票的区别在哪里

    期货和股票的区别在哪里 股票与期货有什么区别 1 概念不同 股票是股份公司发行的所有权凭证 是各个股东作为持股凭证的一种有价证券 而期货属于一种标准化可交易合约 一种到期必须执行的合约 2 交易场所不同 股票是需要在证券交易所进行交易的 例
  • Netty实战(一)Nett的概念及体系结构

    Nett的概念及体系结构 第一章 Java网络编程 1 1 Java NIO 1 2 选择器 第二章 Netty是什么 2 1 Netty简介 2 2 Netty的特性 2 2 1 设计 2 2 2 易于使用 2 2 3 性能 2 2 4
  • 读取文件最后N行

    转自 http www zuidaima com share 1550463669226496 htm 指定行数 可以获取到从这行到文件尾的所有行 分享自大熊 源文件 读取最后10行结果 代码下载地址 http www zuidaima c
  • C++的智能指针unique_ptr、shared_ptr和weak_ptr

    C 的智能指针是一种特殊的指针类型 它能够自动管理内存资源 避免常见的内存泄漏和多次释放等问题 C 11引入了三种主要的智能指针 unique ptr shared ptr和weak ptr 目录 unique ptr shared ptr
  • 双线性插值算法的详细总结

    原文出处 http blog csdn net xjz18298268521 article details 51220576 最近在做视频拼接的项目 里面用到了图像的单应性矩阵变换 在最后的图像重映射 由于目标图像的坐标是非整数的 所以需
  • pcie设备之驱动加载udev事件详解

    打卡打卡 udev 自内核2 6之后取代devfs udev配置 usr lib udev rules d etc udev rules d 如何触发udev事件 kobject uevent函数 pcie scan扫描函数 初始化pcie
  • python在linux系统下的编辑编译运行

    PYTHON在linux系统下的编辑编译 新建一个脚本文件 编写程序 运行程序 若安装了如spyder这样的编辑器 若是很简单的代码 新建一个脚本文件 gedit xxx py 新建py格式文件 编写程序 运行程序 在当前目录下 输入pyt
  • 【论文学习】FD-MonbileNet: IMPROVED MOBILENET WITH A FAST DOWNSAMPLING STRATEGY

    原文链接 https arxiv org abs 1802 03750 作者介绍了一种高效且在有限运算量限制上十分准确的网络 Fast Downsampling MobileNet FD MobileNet 其中心思想是在MobileNet
  • 中国钢铁产业产量分析与市场需求状况研究报告2022版

    中国钢铁产业产量分析与市场需求状况研究报告2022版 修订日期 2021年12月 搜索鸿晟信合研究院查看官网更多内容 第一章 产业转移的内涵及模式概述 1 1 产业转移的概念界定 1 1 1 产业转移的定义 1 1 2 产业转移的分类 1
  • mysql 连续打卡_MySQL查询连续打卡信息?

    最近多次看到用SQL查询连续打卡信息问题 自己也实践一波 抛开问题本身 也是对MySQL窗口函数和自定义变量用法的一种练习 01 建表 所用数据库为MySQL8 0 简单而不失一般性 建立一个仅有记录id 用户id 日期和打卡标记共4个字段