Oracle层次查询和分析函数在号段选取中的应用
2011-02-11 09:05
459 查看
1. 问题的提出
在实际工作中,我们常常会碰到号段选取的问题。
(1)一组连续的数,去掉中间一些数,要求出剩下的数的区间(即号段)。
例如:一串数字为1,2,3,4,7,9,10,则号段为1-4,7-7,9-10
(2)知道号段的起止,要求出该号段内所有的数。
例如:号段为1-3,15-15,则号段内所有的数为1,2,3,15
(3)一组数,中间可能有断点,要求出缺失的数。
例如:一串数字为1,2,3,4,7,9,10,则缺失的数为5,6,8
(4)已知大号段范围及已用号段范围,求可用号段范围。
例如:大号段范围0-999,已用号段范围0-200,399-599,则可用号段范围为201-398,600-999
2. 基础知识
先做下热身运动,回顾一下层次查询和lead/lag函数的运用。
2.1 伪列rownum和level
伪列就是并非在表中真正存在的列。已有很多资料介绍rownum和level这两个伪列。这里只想强调一点,伪列是只针对结果集的。
2.2 利用层次查询构造连续的数
产生5~8这4个连续的数:
以8月为界,例如2005年8月1日,之前的在校学生入学年份为2001~2004,之后的为2002~2005。求当前日期下的在校学生入学年份:
2.3 用分析函数Lead和Lag获得相邻行的字段值
简单的说,在这里,Lag是获得前一行的内容,而Lead是获得后一行的内容。
这里,通过指定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
.1.2 解答
思路:利用lead取得下一行的kshm,然后和本行的kshm想比,如果差值为1,说明这一行和下一行是连续的。由于首尾的特殊性,故而需要先用max和min来获得首尾点。
3.2 根据号段求出包含的数
3.2.1 题例
有表及测试数据如下:
S为号段起点,E为号段终点,求出起点和终点之间的数(包括起点和终点)。
3.2.2 解答
很明显,这需要构造序列来解决问题。
运行结果:
我们再看下面这种做法:
嗯,得到的结果也是正确的,若我们把粗斜体字部分去掉后,看看结果是什么样:
这样的结果,显然不是我们需要的,更何况,这是错误的。由此更能深入理解,伪列是只针对结果集的。
在实际工作中,我们常常会碰到号段选取的问题。
(1)一组连续的数,去掉中间一些数,要求出剩下的数的区间(即号段)。
例如:一串数字为1,2,3,4,7,9,10,则号段为1-4,7-7,9-10
(2)知道号段的起止,要求出该号段内所有的数。
例如:号段为1-3,15-15,则号段内所有的数为1,2,3,15
(3)一组数,中间可能有断点,要求出缺失的数。
例如:一串数字为1,2,3,4,7,9,10,则缺失的数为5,6,8
(4)已知大号段范围及已用号段范围,求可用号段范围。
例如:大号段范围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); |
select * from (select to_char(add_months(sysdate, 4), 'yyyy') - rownum from dual connect by rownum<5); |
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 |
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 |
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
.1.2 解答
思路:利用lead取得下一行的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.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; |
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 88 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 94 |
相关文章推荐
- Oracle层次查询和分析函数在号段选取中的应用
- Oracle层次查询和分析函数在号段选取中的应用
- Oracle层次查询和分析函数在号段选取中的应用
- Oracle层次查询和分析函数在号段选取中的应用
- 层次查询和分析函数(LAG、LEAD)在号段选取中的应用
- 层次查询和分析函数(LAG、LEAD)在号段选取中的应用
- Oracle层次查询和分析函数
- Oracle兼容之层次查询:层次查询高级特性分析-性能调优
- Oracle层次查询及应用(start with connect by)
- Oracle应用专题之:分析函数3(Top/Bottom N、First/Last、NTile)
- Oracle高级查询之LAG和LEAD分析函数
- ORACLE分析函数--row_number()应用
- ORACLE分析函数--row_number()应用
- oracle分析函数row_number的实例应用
- oracle 分析函数应用
- 使用Oracle 分析函数RANK()实现查询库存中每类物资最新N条记录
- Oracle分析函数应用(Analytic Functions)
- oracle分析函数应用
- oracle利用分析函数row_number()over()查询一张表所有字段并按照其中部分字段分组查询某字段最大值
- Oracle应用专题之:分析函数3(Top/Bottom N、First/Last、NTile)