当前位置: 首页 > news >正文

SQL实战:02之连续数问题求解

文章目录

  • 概述
  • 题目:体育馆的人流量
  • 题解
    • 步骤一:构造出一个连续序列
    • 步骤二:找出符合条件的组的序号
    • 步骤三:fetch结果,使用内连接过滤出符合条件的记录。
    • 完整SQL
  • 题目二:连续出现的数字
  • 题解
    • 步骤一:分区并构建连续的序列
    • 步骤二:使用id减去连续序列
    • 步骤三:分组统计得出次数
    • 完整SQL
    • 其他解法

概述

最近刷题时遇到了一些有意思的题,查询连续出现多次的数或者最长的连续序列,而且出现了多次,对于这种题的实现思想就是利用等差数列的思想来解决。

一个连续的序列减一个连续的序列的差值一定是相同的。

例如有一个整数序列A:[4,5,6,7,8,10,11,12,15,19] 可以看到其中有些数据是连续的,有些不是连续的。为了找出这些 连续的序列可以按照从小到大给这组数排序,得到一个排名序列B,如下: 
[1,2,3,4,5,6,7,8,9,10]利用序列A - 序列B得到的答案是[3,3,3,3,3,4,4,4,6,9]可以很明显的看出只要是连续的值得到的差值都是相同的。

利用这个思想,就可以很方便的解决这类问题。

题目:体育馆的人流量

表:Stadium
+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| id            | int     |
| visit_date    | date    |
| people        | int     |
+---------------+---------+
visit_date 是该表中具有唯一值的列。
每日人流量信息被记录在这三列信息中:序号 (id)、日期 (visit_date)、 人流量 (people)
每天只有一行记录,日期随着 id 的增加而增加编写解决方案找出每行的人数大于或等于 100 且 id 连续的三行或更多行记录。返回按 visit_date 升序排列 的结果表。查询结果格式如下所示。示例 1:输入:
Stadium 表:
+------+------------+-----------+
| id   | visit_date | people    |
+------+------------+-----------+
| 1    | 2017-01-01 | 10        |
| 2    | 2017-01-02 | 109       |
| 3    | 2017-01-03 | 150       |
| 4    | 2017-01-04 | 99        |
| 5    | 2017-01-05 | 145       |
| 6    | 2017-01-06 | 1455      |
| 7    | 2017-01-07 | 199       |
| 8    | 2017-01-09 | 188       |
+------+------------+-----------+
输出:
+------+------------+-----------+
| id   | visit_date | people    |
+------+------------+-----------+
| 5    | 2017-01-05 | 145       |
| 6    | 2017-01-06 | 1455      |
| 7    | 2017-01-07 | 199       |
| 8    | 2017-01-09 | 188       |
+------+------------+-----------+
解释:
id 为 5、6、7、8 的四行 id 连续,并且每行都有 >= 100 的人数记录。
请注意,即使第 7 行和第 8 行的 visit_date 不是连续的,输出也应当包含第 8 行,因为我们只需要考虑 id 连续的记录。
不输出 id 为 2 和 3 的行,因为至少需要三条 id 连续的记录。

题解

因为要求解id连续的三行或者更多行的记录,且每行人数大于或等于100,这个就是典型的 求解 连续序列的问题。对于这类问题的核心思想就是利用等差数列来求解,一个连续递增的序列减另一个连续递增的序列,得到的差值是相同的。

步骤一:构造出一个连续序列

利用row_number() 函数,按照id的升序排列构造出一个连续的序列(1,2,3,4,5等等)并且过滤出人数大于等于100的行。再使用id减去row_number,会得到一个值

with grouped_table AS(SELECT id,id - row_number() over(order by id ASC) AS groupID,visit_date,peopleFROM Stadium where people >= 100
)

步骤二:找出符合条件的组的序号

第一步ID减去row_number后,如果id是连续的,那么得到的差值group_id一定是相等的,所以可以通过groupID来做分组查询,统计出连续序列的长度,并过滤出连续的三行或者更多行的groupID。

hinted_group_table As(SELECT  groupID from grouped_table group by groupID having count(*) >= 3
)

步骤三:fetch结果,使用内连接过滤出符合条件的记录。

SELECT id,visit_date,people FROM grouped_table T1,hinted_group_table T2 where T1.groupID = T2.groupID;

完整SQL

with grouped_table AS(SELECT id,id - row_number() over(order by id ASC) AS groupID,visit_date,peopleFROM Stadium where people >= 100
),hinted_group_table As(SELECT  groupID from grouped_table group by groupID having count(*) >= 3
)
SELECT id,visit_date,people FROM grouped_table T1,hinted_group_table T2 where T1.groupID = T2.groupID;

题目二:连续出现的数字

表:Logs+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| id          | int     |
| num         | varchar |
+-------------+---------+
在 SQL 中,id 是该表的主键。
id 是一个自增列。找出所有至少连续出现三次的数字。返回的结果表中的数据可以按 任意顺序 排列。结果格式如下面的例子所示:示例 1:输入:
Logs 表:
+----+-----+
| id | num |
+----+-----+
| 1  | 1   |
| 2  | 1   |
| 3  | 1   |
| 4  | 2   |
| 5  | 1   |
| 6  | 2   |
| 7  | 2   |
+----+-----+
输出:
Result 表:
+-----------------+
| ConsecutiveNums |
+-----------------+
| 1               |
+-----------------+
解释:1 是唯一连续出现至少三次的数字。

题解

步骤一:分区并构建连续的序列

按照num分区,并按照id排序,将相同数字划分到统一窗口中,生成一个连续的排序序号。

with t1 AS (SELECT id, num, row_number() over(partition by num order by id) AS rn from  Logsorder by num asc,id asc
),

输出

| id | num | rn |
| -- | --- | -- |
| 1  | 1   | 1  |
| 2  | 1   | 2  |
| 3  | 1   | 3  |
| 5  | 1   | 4  |
| 4  | 2   | 1  |
| 6  | 2   | 2  |
| 7  | 2   | 3  |

步骤二:使用id减去连续序列

select id,num,id-rn AS rn from t1

输出

| id | num | rn |
| -- | --- | -- |
| 1  | 1   | 0  |
| 2  | 1   | 0  |
| 3  | 1   | 0  |
| 5  | 1   | 1  |
| 4  | 2   | 3  |
| 6  | 2   | 4  |
| 7  | 2   | 4  |

从上面的数据既可以看到数字1:连续出现的数字1的id减去连续的排名序列时的值都为0.

步骤三:分组统计得出次数

利用差值和num分组统计出现次数就可以得出连续出现的数字

select distinct num AS ConsecutiveNums from t1 group by num,rn having count(*) >= 3;

输出

| ConsecutiveNums |
| --------------- |
| 1               |

完整SQL

with t1 AS (SELECT id, num, row_number() over(partition by num order by id) AS rn from  Logsorder by num asc,id asc
),
t2 AS (select id,num,id-rn AS rn from t1
)select distinct num AS ConsecutiveNums from t2 group by num,rn having count(*) >= 3;

其他解法

这道题还有其他解法,因为只需要统计出 连续出现至少3次的数字,所以可以通过下移来解决,如下所示:

with  t1 AS (select id,num,lag(num,1) over(order by id) as prev_num from Logs
),
t2 as (select id,num,prev_num,lag(prev_num,1) over(order by id) as prev_num2 from t1
)
select distinct num as ConsecutiveNums from t2 where num-prev_num=0 and num-prev_num2=0;

当然这种方式 有一个局限性,仅仅适用于连续出现次数比较少的场景,需要下移n-1次。

相关文章:

  • 基于单片机的游泳馆智能管理系统
  • 借助deepseek和vba编程实现一张表格数据转移到多张工作簿的表格中
  • LVGL在VScode的WSL2中仿真
  • Linux之安装配置Nginx
  • Redis--预备知识以及String类型
  • 【AI 加持下的 Python 编程实战 2_10】DIY 拓展:从扫雷小游戏开发再探问题分解与 AI 代码调试能力(中)
  • 双目视觉中,利用左右图像互补信息来补全彼此缺失区域
  • ETL 数据集成都包含哪些?
  • 【金仓数据库征文】——金仓数据库:国产数据库的卓越之选
  • 开发并发布一个属于自己的包(npm)
  • 卸载rpm包
  • 2. Linux开发工具
  • 亚远景-如何通过ASPICE评估满足功能安全(ISO 26262)合规需求?
  • OFDM 信道表示(3)
  • 【网络安全】网络钓鱼的类型
  • React:什么是Hook?通俗易懂的讲讲
  • MuJoCo 机械臂 PPO 强化学习逆向运动学(IK)
  • 分页查询优惠券
  • 逐步了解蓝牙 LE 配对(物联网网络安全)
  • Missashe考研日记-day26
  • 李家超称香港将部署为内地企业提供供应链服务,突破美国封锁
  • 研究|和去年相比,人们使用AI的需求发生了哪些变化?
  • “茉上茶田”傍大牌诱导加盟续:违规从事特许经营被罚没670余万元
  • 2024年度全国十大考古新发现公布,武王墩一号墓等入选
  • 人民日报:外卖平台应保障好骑手就业权益,消除后顾之忧
  • 920余名在缅甸当阳等地实施跨境电信网络诈骗的中国籍犯罪嫌疑人被移交我方