层次查询和分析函数(LAG、LEAD)在号段选取中的应用

2023-09-21 11:13:20

1. 问题的提出

在实际工作中,我们常常会碰到号段选取的问题,例如:
一组连续的数,去掉中间一些数,要求出剩下的数的区间(即号段)例如:一串数字为1,2,3,4,7,9,10,则号段为1-4,7-7,9-10
知道号段的起止,要求出该号段内所有的数例如:号段为1-3,15-15,则号段内所有的数为1,2,3,15
一组数,中间可能有断点,要求出缺失的数例如:一串数字为1,2,3,4,7,9,10,则缺失的数为5,6,8
已知大号段范围及已用号段范围,求可用号段范围例如:大号段范围0-999,已用号段范围0-200,399-599,则可用号段范围为201-398,600-999

2. 基础知识

先回顾一下层次查询和lead/lag函数的运用

2.1 伪列rownum和level

伪列就是并非在表中真正存在的列。已有很多资料介绍rownum和level这两个伪列。这里只想强调一点,伪列是只针对结果集的。

2.2 利用层次查询构造连续的数

产生5~8这4个连续的数
select * from (select rownum+4 from dual connect by rownum<5);
select * from (select level+4 from dual connect by level<5);
以8月为界,例如2005年8月1日,之前的在校学生入学年份为2001~2004,之后的为2002~2005。求当前日期下的在校学生入学年份:
select * from (select to_char(add_months(sysdate, 4), 'yyyy') - rownum from dual connect by rownum<5);

2.3 用分析函数Lead和Lag获得相邻行的字段值

select rn, lag(rn)over(order by rn) previos, lead(rn)over(order by rn) next from (select rownum+4 rn from dual connect by rownum<5);
RN PREVIOS NEXT
---------- ---------- ----------
5 ? ?6
6 5 7
7 6 8
8 7
简单的说,在这里,Lag是获得前一行的内容,而Lead是获得后一行的内容。
select rn, lag(rn,2,-1)over(order by rn) previos, lead(rn,2,-1) over(order by rn) next from (select rownum+4 rn from dual connect by rownum<5);
RN PREVIOS NEXT
---------- ---------- ----------
5 -1 7
6 -1 8
7 5 -1
8 6 -1
这里,通过指定offset参数来获得两行前的内容和两行后的内容,如果offset超出范围并且未设定默认值-1,那么系统会自动将其值设为NULL。

3. 问题的解决

3.1 已知号码求号段

3.1.1 题例

构造表及数据
create table T(FPHM VARCHAR2(10),KSHM VARCHAR2(32));
insert into t (FPHM, KSHM) values ('2014', '00000001');
insert into t (FPHM, KSHM) values ('2014', '00000002');
insert into t (FPHM, KSHM) values ('2014', '00000003');
insert into t (FPHM, KSHM) values ('2014', '00000004');
insert into t (FPHM, KSHM) values ('2014', '00000005');
insert into t (FPHM, KSHM) values ('2014', '00000007');
insert into t (FPHM, KSHM) values ('2014', '00000008');
insert into t (FPHM, KSHM) values ('2014', '00000009');
insert into t (FPHM, KSHM) values ('2013', '00000120');
insert into t (FPHM, KSHM) values ('2013', '00000121');
insert into t (FPHM, KSHM) values ('2013', '00000122');
insert into t (FPHM, KSHM) values ('2013', '00000124');
insert into t (FPHM, KSHM) values ('2013', '00000125');
COMMIT;
怎样能查询出来这样的结果,查询出连续的记录来。

3.1.2 解答

思路:利用lag取得前一行的kshm,然后和本行的kshm想比,如果差值为1,说明这一行和上一行是连续的。由于首尾的特殊性,故而需要先用max和min来获得首尾点。
select fphm, nvl(lag(e)over(partition by fphm order by s),minn) ST, nvl(S,maxn) EN from
(select fphm, lag(kshm,1) over(partition by fphm order by kshm) S, kshm E, min(kshm)over(partition by fphm) minn, max(kshm) over(partition by fphm) maxn from t)
where nvl(E-S-1,1)<>0;
FPHM ST EN
---------- ---------- ----------
2013 00000120 00000122
2013 00000124 00000125
2014 00000001 00000005
2014 00000007 00000009

3.2 根据号段求出包含的数

3.2.1 题例

有表及测试数据如下:
CREATE TABLE T20(ID NUMBER(2), S NUMBER(5), E NUMBER(5));
INSERT INTO T20 ( ID, S, E ) VALUES ( 1, 10, 11);
INSERT INTO T20 ( ID, S, E ) VALUES ( 2, 1, 5);
INSERT INTO T20 ( ID, S, E ) VALUES ( 3, 88, 92);
COMMIT;
S为号段起点,E为号段终点,求出起点和终点之间的数(包括起点和终点)

3.2.2 解答

很明显,这需要构造序列来解决问题
select a.id, a.s, a.e,b.dis, a.S+b.dis-1 h from
t20 a,
?(select rownum dis from
?(select max(e-s)+1 gap from t20)
connect by rownum<=gap) b
where a.e>=a.s+b.dis-1
order by a.id, 4
运行结果:
ID S E DIS H
---------- ---------- ---------- ---------- ----------
1 10 11 1 10
1 10 11 2 11
2 1 5 1 1
2 1 5 2 2
2 1 5 3 3
2 1 5 4 4
2 1 5 5 5
3 88 92 1 8
3 88 92 2 89
3 88 92 3 90
3 88 92 4 91
3 88 92 5 92
?
我们再看下面这种做法:
select a.id, a.s, a.e,rownum, a.S+rownum-1 h from
t20 a ,
?(select id, e-s+1 gap from t20 where id=2) b
where a.id=b.id
connect by rownum<=gap
ID S E ROWNUM H
---------- ---------- ---------- ---------- ----------
2 1 5 1 1
2 1 5 2 2
2 1 5 3 3
2 1 5 4 4
2 1 5 5 5
?
得到的结果也是正确的,若我们把粗斜体字部分去掉后,看看结果是什么样:
ID S E ROWNUM H
---------- ---------- ---------- ---------- ----------
1 10 11 1 10
1 10 11 2 11
2 1 5 3 3
2 1 5 4 4
2 1 5 5 5
2 1 5 6 6
3 88 92 7 9

这样的结果,显然不是我们需要的,更何况,这是错误的。由此更能深入理解,伪列是只针对结果集的。

3.3 求缺失的号

3.3.1 题例

table T,列:serial_no
我想能够查询一下serial_no这个字段的不连续的值。
例如:
serial_no
1
2
3
4
6
8
9
10
我想一个sql语句查出来缺失的号码,
显示结果为:
5
7

3.3.2 解答

思路:找出数B和它前面的数A进行比较(数按从大到小进行排序),如果B-A=1,则说明是连续的,中间没有断点。
select distinct s+level-1 rlt from (select lag(serial_no,1) over(order by serial_no)+1 S, serial_no-1 E from t) where E-S<>0 connect by level<=e-s

3.4 求尚未使用的号段

3.4.1 题例

表A结构:
bill_type_id varchar2(1),
bill_start number,
bill_end number,
office_level varchar2(4)
数据如下:
A 0 999 1
A 0 199 2
A 300 499 2
A 700 799 2
sql目的是取出包含在level1级别里的,还没有录入level2级别的号段。

3.4.2 解答

这个好像是3.1和3.3这两个问题的逆问题
创建表及测试数据:
CREATE TABLE T8(A NUMBER(4),B NUMBER(4), C NUMBER(4), Q VARCHAR2(1 BYTE));
Insert into T8(A, B, C, Q)Values(555, 666, 2, 'A');
Insert into T8(A, B, C, Q)Values(100, 199, 2, 'A');
Insert into T8(A, B, C, Q)Values(0, 999, 1, 'A');
Insert into T8(A, B, C, Q)Values(300, 499, 2, 'A');
COMMIT;
思路:将大号段的边界与小号段的边界相比,从大号段中将小号段“挖”掉,这样剩下的就是可用号段了。
select S,E from
(
SELECT NVL2(LAG(A)OVER(PARTITION BY Q ORDER BY A), B+1, MIN(A)OVER(PARTITION BY Q)) S,
NVL(LEAD(A)OVER(PARTITION BY Q ORDER BY A)-1, MAX(B)OVER(PARTITION BY Q)) E
from t8 START WITH C=1 CONNECT BY C-1 = PRIOR C AND Q= PRIOR Q
)
where s<=e
运行结果:
S E
---------- ----------
0 99
200 299
500 554
667 999

4. 另外的一个应用

得到每隔一分钟(小时、秒)的序列
SELECT SYSDATE + LEVEL / 24 / 60 FROM DUAL CONNECT BY ROWNUM <= 3;
20100515 10:59I:07
20100515 11:0I:07
20100515 11:1I:07

更多推荐

【面试题】forEach能跳出循环吗?

前端面试题库(面试必备)推荐:★★★★★地址:前端面试题库【国庆头像】-国庆爱国程序员头像!总有一款适合你!如果面试官,或者有人问你foreach怎么跳出循环,请你大声的告诉ta,跳不出!!!!!!!!!!foreach跳不出循环为什么呢?先看看foreach大体实现。Array.prototype.customFor

Zabbix介绍与安装

目录一、概述二、zabbix的主要功能三、zabbix监控原理四、Zabbix监控模式五、zabbix的架构server-clientserver-proxy-clientmaster-node-client六、zabbix的安装安装zabbix服务端安装zabbix客户端测试zabbix1、在Web页面中添加agen

Windows脚本导入导出Mongodb数据库文件实现备份与恢复

导出指定时间范围的数据@echooffsetlocalrem提前将MongoDB安装的bin路径加到系统环境变量PATH里面,方便直接运行mongodump命令ifnotexistD:\MongoDB_backupgotocreategotostart:createrem如果没有创建导出文件目录,那么在此步骤创建mdD

链式法则:概率论描述语言模型

目录1.事件相互独立2.链式法则3.示例4.语言模型中的链式法则1.事件相互独立事件相互独立就是:一个事件的发生与否,不会影响另外一个事件的发生。当a和b两个事件互相独立时,有:P(a|b)=P(a)推广到3个事件就有下面这个公式:P(a|b,c)=P(a|c)其中:P(a|b,c)表示在b和c事件都发生的情况下,a事

特征融合篇 | YOLOv8 引入中心化特征金字塔 EVC 模块 | 《Centralized Feature Pyramid for Object Detection》

论文地址:https://arxiv.org/pdf/2210.02093.pdf代码地址:https://github.com/QY1994-0919/CFPNet视觉特征金字塔在广泛的应用中展示了其在效果和效率上的优越性。然而,现有的方法过于关注层间特征之间的交互,而忽视了层内特征的调节,而这些调节在经验上被证明是

springboot配置注入增强(二)属性注入的原理

一原理1配置的存储springboot在启动的时候会后构建一个org.springframework.core.env.Environment类型的对象,这个对象就是用于存储配置,如图springboot会在启动的最开始创建一个Environment对象这个webApplicationType的枚举是在newSprin

Java面向对象编程

对于IP地址130.63.160.2,MASK为255.255.255.0,子网号为()A.160.2B.160C.63.160D.130.63.160答案:B将网络物理地址转换为IP地址的协议是()A.IPB.ICMPC.ARPD.RARP答案:D有关cookie和session的描述,下面错误的是()A.cooki

如何在外网访问公司项目?快解析实现内网ip让公网连接

随着互联网技术的不断发展,越来越多的企业和个人选择使用服务器进行网站或应用程序的部署。公司内部项目需要提供外网访问是个常见的网络场景,需要怎么操作设置也是网络或项目人员需要关注的。企业使用服务器搭建公司业务系统一般会使用云服务器或者使用本地的服务器,有时候,我们需要让外部网络访问本地服务器。然而,由于大多数服务器都是在

Linux下的系统编程——守护进程、线程(十二)

前言:我们知道进程拥有一个PCB,在Linux中被称为task_struct,并且有一个进程地址空间,也有一个页表,通过页表指向物理内存,但是从今天开始,对进程的概念可能发生变化,这个我们后边来说,在Linux中,并没有真正的线程,而是使用进程的PCB来模拟线程,也就是说一个线程在创建时,只会去创建一个PCB,而这个P

【用unity实现100个游戏之13】复刻类泰瑞利亚生存建造游戏

文章目录前言素材人物瓦片一、建造系统1.定义物品类2.绘制地图3.实现瓦片选中效果4.限制瓦片选择5.放置物品功能6.清除物品7.生成和拾取物品功能二、库存系统源码完结前言本文来实现一个类泰瑞利亚游戏的demo,其中主要包括经典的库存系统和建造系统素材人物https://assetstore.unity.com/pac

使用自定义注解发布webservice服务

使用自定义注解发布webservice服务概要代码自定义注解WebService接口服务发布配置使用结果概要在springboot使用webservice,发布webservice服务的时候,我们经常需要手动在添加一些发布的代码,比如:@BeanpublicEndpointorganizationEndpoint(){

热文推荐