Oracle层次查询和分析函数在号段选取中的应用

转自:http://www.itpub.net/thread-719692-1-1.html

摘要
一组连续的数,去掉中间一些数,如何求出剩下的数的区间(即号段)?知道号段的起止,如何求出该号段内所有的数?知道一个大的号段范围和已经取过的号段,如何求出可用的号段?利用Oracle提供的强大的查询功能以及分析函数,我们可以很轻松的解决上述问题。

关键词:
号段选取、连续数、断点、层次查询、分析函数、connect by、rownum、level、lead、lag

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个连续的数
[php]
select * from (select rownum+4 from dual connect by rownum<5);
select * from (select level+4 from dual connect by level<5);
........
[/php]
?        以8月为界,例如2005年8月1日,之前的在校学生入学年份为2001~2004,之后的为2002~2005。求当前日期下的在校学生入学年份:
[php]
select * from (select to_char(add_months(sysdate, 4), ‘yyyy‘) - rownum from dual connect by rownum<5);
........
[/php]
2.3        用分析函数Lead和Lag获得相邻行的字段值
[php]
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
........
[/php]
简单的说,在这里,Lag是获得前一行的内容,而Lead是获得后一行的内容。
[php]
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
........
[/php]
这里,通过指定offset参数来获得两行前的内容和两行后的内容,如果offset超出范围并且未设定默认值-1,那么系统会自动将其值设为NULL。
3.        问题的解决
有了基础知识的积累,我们就可以解决前面提到的问题。
3.1        已知号码求号段
3.1.1        题例
我有一个表结构,
fphm,kshm
2014,00000001
2014,00000002
2014,00000003
2014,00000004
2014,00000005
2014,00000007
2014,00000008
2014,00000009
2013,00000120
2013,00000121
2013,00000122
2013,00000124
2013,00000125

(第二个字段内可能是连续的数据,可能存在断点。)

怎样能查询出来这样的结果,查询出连续的记录来。
就像下面的这样?
2014,00000001,00000005
2014,00000009,00000007
2013,00000120,00000122
2013,00000124,00000125

3.1.2        解答
思路:利用lag取得前一行的kshm,然后和本行的kshm想比,如果差值为1,说明这一行和上一行是连续的。由于首尾的特殊性,故而需要先用max和min来获得首尾点。
[php]
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

........
[/php]
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        解答
很明显,这需要构造序列来解决问题
[php]
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         88
         3         88         92          2         89
         3         88         92          3         90
         3         88         92          4         91
         3         88         92          5         92

........
[/php]
我们再看下面这种做法:

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

[php]
        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

........
[/php]
嗯,得到的结果也是正确的,若我们把粗斜体字部分去掉后,看看结果是什么样:
[php]
        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         94
........
[/php]
这样的结果,显然不是我们需要的,更何况,这是错误的。由此更能深入理解,伪列是只针对结果集的。
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,则说明是连续的,中间没有断点。
[php]
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
........
[/php]
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;

思路:将大号段的边界与小号段的边界相比,从大号段中将小号段“挖”掉,这样剩下的就是可用号段了。
[php]
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        
........

Oracle层次查询和分析函数在号段选取中的应用

时间: 2024-10-16 01:21:58

Oracle层次查询和分析函数在号段选取中的应用的相关文章

oracle层次查询

1 定义: 层次查询使用树的遍历,走遍含树形结构的数据集合,来获取树的层次关系报表的方法 树形结构的父子关系,你可以控制: ① 遍历树的方向,是自上而下,还是自下而上 ②  确定层次的开始点(root)的位置 层次查询语句正是从这两个方面来确定的,start with确定开始点,connect by确定遍历的方向  www.2cto.com 2 语法: 注释: ① level是伪列,表示等级 ② from后面只能是一个表或视图,对于from是视图的,那么这个view不能包含join ③ Wher

Oracle 模糊查询方法

在这个信息量剧增的时代,如何帮助用户从海量数据中检索到想要的数据,模糊查询是必不可少的.那么在Oracle中模糊查询是如何实现的呢? 一.我们可以在where子句中使用like关键字来达到Oracle模糊查询的效果:在Where子句中,可以对datetime.char.varchar字段类型的列用Like关键字配合通配符来实现模糊查询,以下是可使用的通配符: (1)% :零或者多个字符,使用%有三种情况 字段 like '%关键字%'字段包含"关键字"的记录 字段 like '关键字%

最新手机号码归属地数据库 联通号段 移动号段 电信号段 170号段 手机号段查询 2014年6月 300635条

最新手机号码归属地数据库 每月更新一次移动号段: 134 135 136 137 138 139 147 150 151 152 157 158 159 178 182 183 184 187 188 联通号段:130 131 132 145 155 156 176 185 186 电信号段:133 153 177 180 181 189虚拟运营商:170 2014年6月 300635条 更新历史: 2014-05-01 299920条记录2014-04-01 296010条记录2014-03-

Oracle分区表的层次查询如何才能用到分区?

最近在调优分区表的层次查询时,发现用不到分区,做了一个实验,发现还是可以用的到的,只是写法上有些要求. drop table test; create table test ( id  number primary key, parent_id number, name varchar2(20), code varchar2(4) ) partition by list(code) ( partition p1 values('0301'), partition p2 values('0302'

oracle分层查询中的start with和connect by(树结构查询)

来源:  http://blog.csdn.net/itmyhome1990/article/details/16338637 ORACLE是一个关系数据库管理系统,它用表的形式组织数据,在某些表中的数据还呈现出树型 结构的联系. 例如有如下案例: 数据为节选,字段值含义分别为税务机构代码.税务机构名称.上级税务机构代码,税务机构级别 select * from extern_dm_swjg查询的时候默认顺序就是上面的顺序,可以看出是混乱的并没有特殊结构特征. 而希望的结果如下图: sj_swj

oracle高级查询(实例基于scott用户四张表)

oracle高级查询(实例基于scott用户四张表) 分组查询 多表查询 子查询 综合实例 ======================================================================= scott用户的四张表(emp,dept,bonus,salgrade) 没有这四张表的可参考http://blog.csdn.net/love_legain/article/details/54311040进行创建 -----------------------

【ORACLE】DUMP转储 redo log , undo段及table段

1.1 使用oradebug --启动任务 oradebug setmypid --设置dump文件的名称标示 alter session set tracefile_identifier=undo --查看dump文件 SQL> oradebug tracefile_name; c:\opt\oracle\product\10.2.0\admin\rundb\udump\rundb_ora_6660_pra1.trc --设置权限 oradebug unlimit --查看可以转储的列表: S

Oracle 连接查询&lt;一&gt;

在Oracle数据库中,连接查询分为交叉连接.内连接.外链接3种类型.其中,交叉连接结果是所有其他连接结果的超集,而外连接结果又是内连接结果的超集. 交叉连接 交叉连接又称为笛卡尔积连接,是两个或多个 表之间的无条件连接.一个表中所有记录分别与其他表中所有记录进行记录.如果进行连接的表分别有n1,n2,n3...条记录,那么交叉连接的结果集中将有n1 x n2 x n3 x...条记录.例如,emp表中有14条记录,dept表中有4条记录,那么两个交叉连接后有56条记录,语句为 SQL> SEL

数据库编程3 Oracle 子查询 insert update delete 事务 回收站 字段操作 企业级项目案例

[本文谢绝转载原文来自http://990487026.blog.51cto.com] <大纲> 数据库编程3 Oracle 子查询 insert update delete 事务 回收站 字段操作 企业级项目案例 实验所用数据表 子查询,解决一步不能求解 查询工资比scott高的员工信息: 子查询知识体系搭建: 解释3,查询部门是sales的员工信息: 方法1:子查询 [方法2]:多表: 优化考虑: 解释4[select],只能放单行子查询 解释4[from] 考题:显示员工姓名,薪水 解释