SQL题——连续问题

1.连续问题的统一思路

复制代码
核心思想:连续变量−排序变量=常数C,根据常数C分组求和,即可判断连续次数

1.首先要有连续的参考列,如果没有,则需要自己构造
2.用row_number()对目标做排序,比如日期
3.排序后做diff,连续的diff一定相同,然后按diff取目标结果即可

2.相关题目

1285 找到连续区间的开始和结束数字

表:Logs

bash 复制代码
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| log_id        | int     |
+---------------+---------+

id 是上表具有唯一值的列。

上表的每一行包含日志表中的一个 ID。

编写解决方案,得到 Logs 表中的连续区间的开始数字和结束数字。

返回结果表按照 start_id 排序。

结果格式如下面的例子。

示例 1:

输入:

Logs 表:

bash 复制代码
+------------+
| log_id     |
+------------+
| 1          |
| 2          |
| 3          |
| 7          |
| 8          |
| 10         |
+------------+

输出:

bash 复制代码
+------------+--------------+
| start_id   | end_id       |
+------------+--------------+
| 1          | 3            |
| 7          | 8            |
| 10         | 10           |
+------------+--------------+

解释:

结果表应包含 Logs 表中的所有区间。

从 1 到 3 在表中。

从 4 到 6 不在表中。

从 7 到 8 在表中。

9 不在表中。

10 在表中。

答案:

sql 复制代码
select   min(log_id) as start_id 
        ,max(log_id) as end_id
from 
    (select *
            ,log_id - row_number() over(order by log_id) as diff
    from logs ) t0
group by diff
order by start_id
;

2173 最多连胜的次数

表: Matches

bash 复制代码
+-------------+------+
| Column Name | Type |
+-------------+------+
| player_id   | int  |
| match_day   | date |
| result      | enum |
+-------------+------+

(player_id, match_day) 是该表的主键(具有唯一值的列的组合)。

每一行包括了:参赛选手 id、 比赛时间、 比赛结果。

比赛结果(result)的枚举类型为 ('Win', 'Draw', 'Lose')。

选手的 连胜数 是指连续获胜的次数,且没有被平局或输球中断。

编写解决方案来计算每个参赛选手最多的连胜数。

结果可以以 任何顺序 返回。

结果格式如下例所示:

示例 1:

输入:

Matches 表:

bash 复制代码
+-----------+------------+--------+
| player_id | match_day  | result |
+-----------+------------+--------+
| 1         | 2022-01-17 | Win    |
| 1         | 2022-01-18 | Win    |
| 1         | 2022-01-25 | Win    |
| 1         | 2022-01-31 | Draw   |
| 1         | 2022-02-08 | Win    |
| 2         | 2022-02-06 | Lose   |
| 2         | 2022-02-08 | Lose   |
| 3         | 2022-03-30 | Win    |
+-----------+------------+--------+

输出:

bash 复制代码
+-----------+----------------+
| player_id | longest_streak |
+-----------+----------------+
| 1         | 3              |
| 2         | 0              |
| 3         | 1              |
+-----------+----------------+

解释:

Player 1:

从 2022-01-17 到 2022-01-25, player 1连续赢了三场比赛。

2022-01-31, player 1 平局.

2022-02-08, player 1 赢了一场比赛。

最多连胜了三场比赛。

Player 2:

从 2022-02-06 到 2022-02-08, player 2 输了两场比赛。

最多连赢了0场比赛。

Player 3:

2022-03-30, player 3 赢了一场比赛。

最多连赢了一场比赛。

答案:

sql 复制代码
select a.player_id
        ,IFNULL(b.longest_streak, 0) as longest_streak
from (
    select distinct player_id as player_id
    from matches
) a
left join 
(
     select player_id
    ,max(cnt) as longest_streak 
    from
        (select player_id
                ,diff
                ,count(1) as cnt
        from
            (select *
                    ,rn1 - rn2 as diff
            from
                (select *
                        ,row_number() over(partition by player_id order by match_day) as rn1
                        ,row_number() over(partition by player_id, result order by match_day) as rn2
                from matches) t
            )t1
        where  result = 'win'
        group by  player_id
                ,diff
        ) t2
    group by player_id
) b
on a.player_id = b.player_id
;

1454 活跃用户

表 Accounts:

sql 复制代码
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| id            | int     |
| name          | varchar |
+---------------+---------+

id 是该表主键(具有唯一值的列)

该表包含账户 id 和账户的用户名.

表 Logins:

bash 复制代码
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| id            | int     |
| login_date    | date    |
+---------------+---------+

该表可能包含重复项.

该表包含登录用户的账户 id 和登录日期. 用户也许一天内登录多次.

活跃用户 是指那些至少连续 5 天登录账户的用户。

编写解决方案, 找到 活跃用户 的 id 和 name。

返回的结果表按照 id 排序 。

结果表格式如下例所示。

示例 1:

输入:

Accounts 表:

bash 复制代码
+----+----------+
| id | name     |
+----+----------+
| 1  | Winston  |
| 7  | Jonathan |
+----+----------+

Logins 表:

bash 复制代码
+----+------------+
| id | login_date |
+----+------------+
| 7  | 2020-05-30 |
| 1  | 2020-05-30 |
| 7  | 2020-05-31 |
| 7  | 2020-06-01 |
| 7  | 2020-06-02 |
| 7  | 2020-06-02 |
| 7  | 2020-06-03 |
| 1  | 2020-06-07 |
| 7  | 2020-06-10 |
+----+------------+

输出:

bash 复制代码
+----+----------+
| id | name     |
+----+----------+
| 7  | Jonathan |
+----+----------+

解释:

id = 1 的用户 Winston 仅仅在不同的 2 天内登录了 2 次, 所以, Winston 不是活跃用户.

id = 7 的用户 Jonathon 在不同的 6 天内登录了 7 次, , 6 天中有 5 天是连续的, 所以, Jonathan 是活跃用户.

答案:

sql 复制代码
select  distinct t3.id
        ,t4.name
from
    (select id
        ,subdate(login_date, rn) as first_date
    from
        (select id
                ,login_date
                ,row_number() over(partition by id order by login_date) as rn
        from
            (select distinct id
                    ,login_date
            from logins ) t1
        ) t2
    group by id
            ,first_date
    having count(*) >= 5) t3
left join accounts t4
on t3.id = t4.id;

1225 报告系统状态的连续日期

表:Failed

bash 复制代码
+--------------+---------+
| Column Name  | Type    |
+--------------+---------+
| fail_date    | date    |
+--------------+---------+

该表主键为 fail_date (具有唯一值的列)。

该表包含失败任务的天数.

表: Succeeded

bash 复制代码
+--------------+---------+
| Column Name  | Type    |
+--------------+---------+
| success_date | date    |
+--------------+---------+

该表主键为 success_date (具有唯一值的列)。

该表包含成功任务的天数.

系统 每天 运行一个任务。每个任务都独立于先前的任务。任务的状态可以是失败或是成功。

编写解决方案找出 2019-01-01 到 2019-12-31 期间任务连续同状态 period_state 的起止日期(start_date 和 end_date)。即如果任务失败了,就是失败状态的起止日期,如果任务成功了,就是成功状态的起止日期。

最后结果按照起始日期 start_date 排序

返回结果样例如下所示:

示例 1:

输入:

Failed table:

bash 复制代码
+-------------------+
| fail_date         |
+-------------------+
| 2018-12-28        |
| 2018-12-29        |
| 2019-01-04        |
| 2019-01-05        |
+-------------------+

Succeeded table:

bash 复制代码
+-------------------+
| success_date      |
+-------------------+
| 2018-12-30        |
| 2018-12-31        |
| 2019-01-01        |
| 2019-01-02        |
| 2019-01-03        |
| 2019-01-06        |
+-------------------+

输出:

bash 复制代码
+--------------+--------------+--------------+
| period_state | start_date   | end_date     |
+--------------+--------------+--------------+
| succeeded    | 2019-01-01   | 2019-01-03   |
| failed       | 2019-01-04   | 2019-01-05   |
| succeeded    | 2019-01-06   | 2019-01-06   |
+--------------+--------------+--------------+

解释:

结果忽略了 2018 年的记录,因为我们只关心从 2019-01-01 到 2019-12-31 的记录

从 2019-01-01 到 2019-01-03 所有任务成功,系统状态为 "succeeded"。

从 2019-01-04 到 2019-01-05 所有任务失败,系统状态为 "failed"。

从 2019-01-06 到 2019-01-06 所有任务成功,系统状态为 "succeeded"。

答案:

sql 复制代码
select *
from
(
    select  'failed' as period_state 
            ,MIN(fail_date) AS start_date
            ,MAX(fail_date) AS end_date
    from
        (   select fail_date
                ,subdate(fail_date, rn) as frist_date
            from 
                (select fail_date
                        ,row_number() over(order by fail_date) as rn
                from failed 
                where fail_date between '2019-01-01' and '2019-12-31'
            ) t0
    ) t1
    group by frist_date
    union all
    select  'succeeded' as period_state 
            ,MIN(success_date) AS start_date
            ,MAX(success_date) AS end_date
    from
        (   select  success_date
                    ,subdate(success_date, rn) as frist_date
            from 
            (   select success_date
                        ,row_number() over(order by success_date) as rn
                from Succeeded  
                where success_date between '2019-01-01' and '2019-12-31'
            ) t0
    ) t1
    group by frist_date
) t
order by start_date 

2292 连续两年有3个及以上订单的产品

表: Orders

sql 复制代码
+---------------+------+
| Column Name   | Type |
+---------------+------+
| order_id      | int  |
| product_id    | int  |
| quantity      | int  |
| purchase_date | date |
+---------------+------+

order_id 包含唯一值。

该表中的每一行都包含订单 ID、购买的产品 ID、数量和购买日期。

编写解决方案,获取连续两年订购三次或三次以上的所有产品的 id。

以 任意顺序 返回结果表。

结果格式示例如下。

示例 1:

输入:

Orders 表:

sql 复制代码
+----------+------------+----------+---------------+
| order_id | product_id | quantity | purchase_date |
+----------+------------+----------+---------------+
| 1        | 1          | 7        | 2020-03-16    |
| 2        | 1          | 4        | 2020-12-02    |
| 3        | 1          | 7        | 2020-05-10    |
| 4        | 1          | 6        | 2021-12-23    |
| 5        | 1          | 5        | 2021-05-21    |
| 6        | 1          | 6        | 2021-10-11    |
| 7        | 2          | 6        | 2022-10-11    |
+----------+------------+----------+---------------+

输出:

sql 复制代码
+------------+
| product_id |
+------------+
| 1          |
+------------+

解释:

产品 1 在 2020 年和 2021 年都分别订购了三次。由于连续两年订购了三次,所以我们将其包含在答案中。

产品 2 在 2022 年订购了一次。我们不把它包括在答案中。

答案:

sql 复制代码
select distinct product_id
from 
    (select product_id
            ,ord_date
            ,ord_date - row_number() over(partition by product_id order by ord_date) as first_year
    from
        (
            select date_format(purchase_date, '%Y') as ord_date
                ,product_id
                ,count(order_id) as ord_cnt
            from orders 
            group by product_id
                    ,date_format(purchase_date, '%Y')
            having count(order_id) >= 3
        ) t0
    ) t1
group by product_id 
        ,first_year
having count(*) > 1;
相关推荐
stark张宇1 小时前
MySQL 核心内幕:从索引原理、字段选型到日志机制与外键约束,一篇打通数据库任督二脉
数据库·mysql·架构
倔强的石头_1 小时前
融合数据库架构实践:关系型、JSON与全文检索的“一库多能”深度解析
数据库
星辰员3 小时前
KingbaseES数据库:ksql 命令行用户与权限全攻略,从创建到删除
数据库
谢小飞4 小时前
如何让AI用一个下午开发上架Chrome插件助我摸鱼
前端·chrome
华仔啊17 小时前
千万别给数据库字段加默认值 null!真的会出问题
java·数据库·后端
小溪彼岸19 小时前
谷歌发布Gemini in Chrome,Chrome正式成为AI浏览器
chrome·google
小溪彼岸1 天前
谷歌上线Gemini in Chrome,想免费使用还需打怪升级
chrome·google
随风飘的云2 天前
MySQL的慢查询优化解决思路
数据库
IvorySQL2 天前
PostgreSQL 技术日报 (3月7日)|生态更新与内核性能讨论
数据库·postgresql·开源
赵渝强老师2 天前
【赵渝强老师】金仓数据库的数据文件
数据库·国产数据库·kingbase·金仓数据库