SQL实战:05之间隔连续数问题求解

概述

最近刷题时遇到一些比较有意思的题目,之前多次遇到一些求解连续数的问题,这次遇到了他们的变种,连续数可以间隔指定的数也视为是一个完整的"连续"。针对连续数的这类问题我们之前讲的可以利用等差数列 的思想来解决,然而现在发生了变形一定还能继续使用等差数列来解决吗?答案是否定的,针对带有指定间隔的连续数问题应当使用分组问题的思想来解决。

题目:间隔连续表

某游戏公司记录的用户每日登录数据

表logs

字段名 数据类型
id bigint
dt date

输入数据如下:

id dt
1001 2021/12/12
1002 2021/12/12
1001 2021/12/13
1001 2021/12/14
1001 2021/12/16
1002 2021/12/16
1001 2021/12/19
1002 2021/12/17
1001 2021/12/20

计算每个用户最大的连续登录天数,可以间隔一天。解释:如果一个用户在 1,3,5,6 登
录游戏,则视为连续 6 天登录。

题解

第一步:下移数据

SQL 复制代码
WITH temp_001 AS (
    SELECT id
        ,dt
        ,LAG(dt,1,'1970-01-01') OVER (PARTITION BY id ORDER BY dt ASC) AS prev_dt
    FROM logs
)

输出如下:

id dt prev_dt
1001 2021/12/12 1970/01/01
1001 2021/12/13 2021/12/12
1001 2021/12/14 2021/12/13
1001 2021/12/16 2021/12/14
1001 2021/12/19 2021/12/19
1001 2021/12/20 2021/12/20
1002 2021/12/12 1970/01/01
1002 2021/12/16 2021/12/12
1002 2021/12/17 2021/12/16

步骤二:窗口分组累加

计算当前日期和上一日期的差值,如果差值小于等于2,则认为是同一组组号不变,如果差值大于2则任务是新的组,组号+1.

SQL实现:

sql 复制代码
temp_002 AS (
    SELECT id
        ,dt
        ,SUM(IF(DATEDIFF(dt,prev_dt)>2,1,0)) OVER(PARTITION BY id ORDER BY dt ASC) AS group_id
    FROM temp_001
)

输出结果:

id dt group_id
1001 2021/12/12 1
1001 2021/12/13 1
1001 2021/12/14 1
1001 2021/12/16 1
1001 2021/12/19 2
1001 2021/12/20 2
1002 2021/12/12 1
1002 2021/12/16 2
1002 2021/12/17 2

步骤三:根据id和group分组统计出最大、最小日期

sql 复制代码
temp_003 AS (
    SELECT id
        ,group_id
        ,min(dt) as min_dt
        ,max(dt) as max_dt
    FROM temp_002
    GROUP BY id,group_id
)

输出结果

id group_id min_dt max_dt
1001 1 2021/12/12 2021/12/16
1001 2 2021/12/19 2021/12/20
1002 1 2021/12/12 2021/12/12
1002 2 2021/12/16 2021/12/17

步骤四:使用最大日期减掉最小日期再加1,就可以得到每一组的最大连续天数

sql 复制代码
temp_004 AS (
    SELECT id
        ,group_id
        ,min_dt
        ,max_dt
        ,datediff(max_dt,min_dt) + 1 AS days
    FROM temp_003
)

输出结果

id group_id min_dt max_dt days
1001 1 2021/12/12 2021/12/16 5
1001 2 2021/12/19 2021/12/20 2
1002 1 2021/12/12 2021/12/12 1
1002 2 2021/12/16 2021/12/17 2

步骤五: 在取每个id的最大连续天数得到最终结果

sql 复制代码
SELECT id,max(days) as days from temp_004 group by id;

完整SQL

sql 复制代码
WITH temp_001 AS (
    SELECT id
        ,dt
        ,LAG(dt,1,'1970-01-01') OVER (PARTITION BY id ORDER BY dt ASC) AS prev_dt
    FROM logs
)
,temp_002 AS (
    SELECT id
        ,dt
        ,SUM(IF(DATEDIFF(dt,prev_dt)>2,1,0)) OVER(PARTITION BY id ORDER BY dt ASC) AS group_id
    FROM temp_001
)
,temp_003 AS (
    SELECT id
        ,group_id
        ,min(dt) as min_dt
        ,max(dt) as max_dt
    FROM temp_002
    GROUP BY id,group_id
)
,temp_004 AS (
    SELECT id
        ,group_id
        ,min_dt
        ,max_dt
        ,datediff(max_dt,min_dt) + 1 AS days
    FROM temp_003
)
SELECT id,max(days) as days from temp_004 group by id;
相关推荐
不剪发的Tony老师1 小时前
Mathesar:一款基于PostgreSQL的在线电子表格
数据库·postgresql·电子表格
万邦科技Lafite4 小时前
京东按图搜索京东商品(拍立淘) API (.jd.item_search_img)快速抓取数据
开发语言·前端·数据库·python·电商开放平台·京东开放平台
Giser探索家4 小时前
无人机桥梁巡检:以“空天地”智慧之力守护交通生命线
大数据·人工智能·算法·安全·架构·无人机
金仓拾光集4 小时前
__金仓数据库平替MongoDB实战:从多模兼容到高可用落地__
数据库·mongodb·数据库平替用金仓·金仓数据库
北邮-吴怀玉4 小时前
6.1.2.2 大数据方法论与实践指南-离线任务SQL 任务开发规范
大数据·数据库·sql
流烟默4 小时前
MongoDB索引创建语法分析
数据库·mongodb
金仓拾光集4 小时前
__国产化转型实战:制造业供应链物流系统从MongoDB至金仓数据库迁移全指南__
数据库·mongodb·数据库平替用金仓·金仓数据库
初学者_xuan4 小时前
零基础新手小白快速了解掌握服务集群与自动化运维(十五)Redis模块-Redis数据库基础
运维·数据库·自动化
小马哥编程4 小时前
【软考架构】案例分析:MongoDB 如何存储非结构化数据以及其矢量化存储的优点。
数据库·mongodb·架构
默 语5 小时前
MySQL中的数据去重,该用DISTINCT还是GROUP BY?
java·数据库·mysql·distinct·group by·1024程序员节·数据去重