48 找到连续区间的开始和结束数字
Opened this issue · 0 comments
astak16 commented
题目
题目连接:找到连续区间的开始和结束数字
每行记录都有一个 log_id
,后来一些 log_id
从 logs
表中删除。
编写一个 SQL 查询得到 logs
表中的连续区间的开始数字和结束数字。
create table logs (log_id int);
insert into logs (log_id) values ('1'),
(2),
(3),
(7),
(8),
(10);
SQL:方法一
select
min(log_id) start_id,
max(log_id) end_id
from (
select
log_id,
log_id - row_number() over() diff
from logs
) temp group by diff
解析
-
log_id - row_number() over() diff
,如何知道一个连续区间的开始和结束数字,首先需要知道这个区间-
一个连续区间内的数
减去某个规律的数
结果应该是一样的 -
基于这个思路会想到,将这些数按照从小到大的顺序进行排列,那么这个数字减去它在这张表中的排名,如果是一个区间内的数,这个结果应该是一样的。
log_id | rank | diff 1 | 1 | 0 3 | 2 | 1 4 | 3 | 1 7 | 4 | 3 8 | 5 | 3
-
将查询出来的结果作为临时表
temp
-
-
使用
min
和max
函数按照diff
进行分组,分别求出start_id
和end_id
SQL:方法二
select
min(log_id) start_id,
max(log_id) end_id
from (
select
log_id,
case when @id = log_id - 1 then @num:=@num else @num:=@num+1 end num,
@id:=log_id
from logs, (select @id:=null, @num:=0) init
) temp group by num;
解析
思路和方法一是一样的,方法一使用窗口函数实现的,方法二是变量的方式去实现的
-
@num
是用来记录一个里连续区间的,@id
用来保存当前log_id
- 初始化
@id = null
,@num = 0
- 当运行第一条数据时
@id = log_id - 1
为null = 1 - 1
结果是false
,所以输出@num:=@num + 1
结果是0 + 1 = 1
,同时再把@id
的设置为当前的log_id
,即@id = 1
- 当运行第二条数据时,
@id = log_id - 1
为1 = 2 - 1
结果是true
,所以输出@num:=@num
结果是1
,同时再把@id
的设置为当前的log_id
,即@id = 2
- 以此类推,直到运行到最后一条记录
select log_id, case when @id = log_id - 1 then @num:=@num else @num:=@num+1 end num, @id:=log_id from logs, (select @id:=null, @num:=0) init -- 结果 log_id | num | @id:=log_id 1 | 1 | 1 2 | 1 | 2 3 | 1 | 3 7 | 2 | 7 8 | 2 | 8 10 | 3 | 10
- 初始化
-
将第一步的结果作为临时表
temp
-
查询临时表
temp
按照num
进行分组,使用min()
,max()
求出start_id
和end_id
SQL:方法三
with l1 as (
select
log_id start_id,
row_number() over(order by log_id) as rank_id
from logs where log_id - 1 not in (select log_id from logs)
),
l2 as (
select
log_id end_id,
row_number() over(order by log_id) as rank_id
from logs where log_id + 1 not in (select log_id from logs)
)
select start_id, end_id from l1 join l2 using(rank_id);
解析
一个连续数字的区间, start_id - 1
和 end_id + 1
肯定不在表中
-
将
log_id - 1
的值肯定不在logs
表中,将这些符合这些值的log_id
查询出来就是start_id
select log_id start_id from logs where log_id - 1 not in (select log_id from logs);
-
同理,
end_id
也是通过这种方法进行查询出来的,end_id + 1
的值肯定不在logs
表中select log_id end_id from logs where log_id + 1 not in (select log_id from logs);
-
将第一步的结果作为临时表
l1
,第二步的结果作为临时表l2
-
将临时表
l1
和l2
进行join
连接,等等,这里好像缺少连接条件?- 一个区间有
start_id
,就一定会有end_id
,所以start_id
和end_id
的数量应该是相同的 - 因为
start_id
和end_id
的数量相同,所以可以使用排序函数分别对l1
和l2
表进行排序row_number() over(order by log_id) as rank_id
- 连接条件就是两表共有的
rank_id
- 一个区间有