oracle 每日一题-分析函数row_number

转自http://www.itpub.net/thread-2084317-1-1.html 感谢newkid

原始出处:
http://www.plsqlchallenge.com/

作者:Kim Berg Hansen

运行环境:SQLPLUS, SERVEROUTPUT已打开

我有一张表保存着每个人的姓名,出生日期(DOB),以及父亲:

create table qz_men (
   man_id    integer primary key
, name      varchar2(10)
, dob       date
, father_id integer references qz_men
)
/

insert into qz_men values (10, 'Zebadiah', date '1950-01-10', null)
/
insert into qz_men values (15, 'William' , date '1970-01-15', 10  )
/
insert into qz_men values (16, 'Stephen' , date '1972-01-16', 10  )
/
insert into qz_men values (20, 'Roger'   , date '1990-01-20', 15  )
/
insert into qz_men values (21, 'Perry'   , date '1992-01-21', 15  )
/
insert into qz_men values (25, 'Louis'   , date '1993-01-25', 16  )
/
insert into qz_men values (26, 'Jerry'   , date '1991-01-26', 16  )
/
commit
/

我想要一张以出生日期排序的清单,显示每个人是他父亲的第几个儿子。

哪些选项包含的查询能够产生如下的输出:

    MAN_ID NAME       DOB        PARENTAGE
---------- ---------- ---------- --------------------
        10 Zebadiah   1950-01-10 Unknown
        15 William    1970-01-15 1. son of Zebadiah
        16 Stephen    1972-01-16 2. son of Zebadiah
        20 Roger      1990-01-20 1. son of William
        26 Jerry      1991-01-26 1. son of Stephen
        21 Perry      1992-01-21 2. son of William
        25 Louis      1993-01-25 2. son of Stephen

(A) 
select m.man_id
     , m.name
     , m.dob
     , nvl2(
          m.father_id
        , row_number() over (
             partition by PRIOR m.man_id
             order by m.dob
          ) || '. son of ' || PRIOR m.name
        , 'Unknown'
       ) as parentage
  from qz_men m
start with m.father_id is null
connect by m.father_id = prior m.man_id
order by m.dob
/

(B) 
select m.man_id
     , m.name
     , m.dob
     , nvl2(
          m.father_id
        , row_number() over (
             partition by PRIOR m.man_id
             order SIBLINGS by m.dob
          ) || '. son of ' || PRIOR m.name
        , 'Unknown'
       ) as parentage
  from qz_men m
start with m.father_id is null
connect by m.father_id = prior m.man_id
order by m.dob
/

(C) 
select m.man_id
     , m.name
     , m.dob
     , nvl2(
          m.father_id
        , row_number() over (
             partition by m.father_id
             order by m.dob
          ) || '. son of ' || (
             select name
               from qz_men f
              where f.man_id = m.father_id
          )
        , 'Unknown'
       ) as parentage
  from qz_men m
start with m.father_id is null
connect by m.father_id = prior m.man_id
order by m.dob
/

(D) 
select m.man_id
     , m.name
     , m.dob
     , nvl2(
          m.father_id
        , row_number() over (
             partition by m.father_id
             order by m.dob
          ) || '. son of ' || (
             select name
               from qz_men f
              where f.man_id = m.father_id
          )
        , 'Unknown'
       ) as parentage
  from qz_men m
order by m.dob
/


(E) 
select s2.man_id
     , s2.name
     , s2.dob
     , nvl2(
          s2.father_id
        , row_number() over (
             partition by s2.father_id
             order by s2.rn
          ) || '. son of ' || s2.father_name
        , 'Unknown'
       ) as parentage
  from (
   select s1.*
        , rownum as rn
     from (
      select m.*
           , prior m.name as father_name
        from qz_men m
      start with m.father_id is null
      connect by m.father_id = prior m.man_id
      order SIBLINGS by m.dob
     ) s1
  ) s2
order by s2.dob
/

(F)
select s1.man_id
     , s1.name
     , s1.dob
     , nvl2(
          s1.father_id
        , row_number() over (
             partition by s1.father_id
             order by s1.rn
          ) || '. son of ' || s1.father_name
        , 'Unknown'
       ) as parentage
  from (
   select m.*
        , prior m.name as father_name
        , row_number() over (
             order SIBLINGS by m.dob
          ) as rn
     from qz_men m
   start with m.father_id is null
   connect by m.father_id = prior m.man_id
  ) s1
order by s1.dob
/


A: (推荐)
在分析函数row_number中我们按照父亲分区,按照每个父亲的孩子的出生日期排序,这样就给出了我们所需的每个孩子的序号。

B: 在CONNECT BY里面,在ORDER BY中使用SIBLING子句,就可以在保留层次顺序的同时,以特定顺序获得树中的兄弟节点,这常常是很有用的。然而在分析函数中是不允许的,所以这个选项会报错:
ORA-30929: ORDER SIBLINGS BY clause not allowed here. 
此外,在这个例子中SIBLINGS是不需要的,因为我们是按父亲分区(如同前一选项)

C: (不推荐)
在分区部分,我们在此处用了FATHER_ID而不是选项A那样的PRIOR用法,这是可以的,会得到相同结果。但是选项A同样用了PRIOR来得到父亲的名字,而此处我们用了一个标量子查询,这样也行,但对于数据库而言是不必要的工作。

D: 如果用了标量子查询而不是PRIOR子句,我们就可以完全去除CONNECT BY,因为所需的结果也不是按层次的顺序排列。选项A用了CONNECT BY来通过PRIOR得到父亲的名字, 这样在遍历树的时候也完成了FATHER_ID的查找。这个选项不需要遍历树,但是在MAN_ID上做查找,这样也许会受益于标量子查询的缓存,这取决于数据。大多数情况下,选项A的性能是最好的,但是取决于树的深度和宽度以及索引情况,在某些特殊情况下这个选项也可能很好。这需要测试。
E: (不推荐)
此处我们在两层的内联视图中用了SIBLINGS(因为ROWNUM是在ORDER之前产生的,我们需要两个层次的内联视图)来在RN列中生成我们需要的序号,然后就可以在我们的分析函数中使用。它没有问题,但是对这个例子是小题大做,因为以父亲分区就不需要层次顺序了。对于其它用例,这可能是个有用的技巧,可以用来保留层次的顺序。
F: 这是在尝试将前一选项简化为一个内联视图而不是双层视图,用的是分析函数ROW_NUMBER而不是ROWNUM。ROW_NUMBER经常是ROWNUM的一个很好的替身,但是在这个例子中它行不通,因为分析函数的ORDER BY不允许SIBLINGS子句,会报错:
ORA-30929: ORDER SIBLINGS BY clause not allowed here.


基础知识可以看http://blog.csdn.net/zhq200902/article/details/59108118

本题新增知识点:ORDER SIBLINGS BY 

原文地址 http://space.itpub.net/519536/viewspace-624176

在层次查询中,如果想让“亲兄弟”按规矩进行升序排序就不得不借助ORDERSIBLINGS BY这个特定的排序语句,若要降序输出可以在其后添加DESC关键字。
通过这个实验给大家展示一下这个“亲兄弟”间的排序功能。

1.回望关系“树”,这棵树中的B和C是亲兄弟,F和G是亲兄弟。注意,D和E不是亲兄弟,最多也就算是堂兄弟,“亲兄弟排序”功能对他们无效。
      A
     / \
    B   C
   /   /
  D   E
 / \
F   G

2.重温一下阐述上图的T表数据
sec@ora10g> select * from t;

X                   Y          Z
---------- ---------- ----------
A                   1
B                   2          1
C                   3          1
D                   4          2
E                   5          3
F                   6          4
G                   7          4

7 rows selected.

3.我们对X列使用“ORDER SIBLINGS BY”进行升序排序,重点关注B和C、F和G的顺序。
sec@ora10g> select lpad(' ',level-1)||x tree, SYS_CONNECT_BY_PATH(x,'/') tree_path from t start with x = 'A' connect by prior y=z order siblings by x;

TREE             TREE_PATH
---------------- ----------------
A                /A
 B               /A/B
  D              /A/B/D
   F             /A/B/D/F
   G             /A/B/D/G
 C               /A/C
  E              /A/C/E

7 rows selected.

4.我们对X列使用“ORDER SIBLINGS BY DESC”进行降序排序,重点关注B和C、F和G的顺序。
sec@ora10g> select lpad(' ',level-1)||x tree, SYS_CONNECT_BY_PATH(x,'/') tree_path from t start with x = 'A' connect by prior y=z order siblings by x desc;

TREE             TREE_PATH
---------------- ----------------
A                /A
 C               /A/C
  E              /A/C/E
 B               /A/B
  D              /A/B/D
   G             /A/B/D/G
   F             /A/B/D/F

7 rows selected.

5.如在层次查询中错误的使用了“ORDER BY”进行排序,则层次查询中蕴含的遍历顺序将被打乱,剩下的将只是信息的罗列。
sec@ora10g> select lpad(' ',level-1)||x tree, SYS_CONNECT_BY_PATH(x,'/') tree_path from t start with x = 'A' connect by prior y=z order by x;

TREE             TREE_PATH
---------------- ----------------
A                /A
 B               /A/B
 C               /A/C
  D              /A/B/D
  E              /A/C/E
   F             /A/B/D/F
   G             /A/B/D/G

7 rows selected.

6.小结
具体问题具体分析,特殊场合特出方法。在层次查询中如需对亲兄弟的先后顺序进行限定,就必须要使用特有的“ORDER SIBLINGS BY”而非“ORDER BY”子句。切记之。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值