一、引言
Oracle数据库系统对标准的SQL语言进行了扩充,其中引入一个称之为层次查询(Hierarchical Queries)的语句。该语句的灵活使用将大大方便我们的一些日常工作。本文将就层次查询(Hierarchical Queries)的概念和使用技巧进行一些探讨,希望有助于读者对层次查询的理解和使用。
二、层次查询的概念
在理解层次查询的概念之前,我们得先了解层次数据的概念。何谓层次数据,象我们日常生活中的单位组织架构,有总经理,总经理之下有部门经理,部门经理之下有普通员工,这些赋有层次属性的数据组织在一起就形成了层次数据。如图一就是一个层次数据,如果按照数据结构中的概念,层次数据就是树。节点a是根节点,没有父亲节点,它有三个子节点分别为b、c、d;k节点有父节点e和子节点r、s、t;w节点只是个叶结点,其只有父节点n,没有子节点。除了根节点,任何节点都有祖先节点,即父节点,父节点的父节点,依次递推到根节点。如在图一中,x节点的祖先节点为o、j、d、a。同理,除了叶结点外,任何节点都有自己的子孙节点。如图一中b到z节点都是a的子孙节点,即都是由节点a繁衍出来的。节点j只有o、p、q、x、y、z六个节点子孙。节点i虽然占据的位置的层次比较高,但它仍然是叶结点,所以它不存在子孙节点。
图一
我们将图一中的数据写入Oracle数据库中一张表,表名为hierarchical_data_test,表结构只有两个字段,分别为node和parent_node(create table hierarchical_data_test ( node char(1),parent_node char(1) null)),表的内容如下表一:
Node |
Parent_node |
a |
|
b |
a |
c |
a |
d |
a |
e |
b |
f |
b |
g |
c |
h |
d |
i |
d |
j |
d |
k |
e |
l |
e |
m |
e |
n |
e |
o |
j |
p |
j |
q |
j |
r |
k |
s |
k |
t |
k |
u |
l |
v |
m |
w |
n |
x |
o |
y |
p |
z |
q |
表一
在象上述含有层次数据的表中,我们通过SELECT语句选择出包含层次信息的数据时,需要使用到下列三个从句: START WITH 使用该语句标示层次数据中使用者选定的根所在行的条件信息; CONNECT BY 使用该语句标示父节点和子节点所在行之间的连接信息; WHERE 使用该语句可以限制和选定返回的层次数据中的特定行,该WHERE语句就是SELECT语句中的WHERE条件语句,读者不要误以为是专为层次查询而设立。
Oracle将按照下列步骤,依据上面提到的三个从句中包含的信息,提取所需要的层次数据返回给用户:
步骤一:Oracle将表中满足START WITH从句中条件的行选取出来,将其作为层次数据的根数据。当然,这些返回的根数据行数完全可以因为条件不止一行。
步骤二:Oracle将依据CONNECT BY从句中条件,选择每一个根行的子行。
步骤三:Oracle将选择子行的后续子行。Oracle先选择步骤二中提到的子行,将其作为根行,选择出它们的子行,然后再将这些子行作为根行,再选择这些行的子行,以此递推。
步骤四:如果层次查询语句中包含WHERE语句,Oracle将从层次数据中移去所有的不满足WHERE条件从句的行。Oracle对结果集中的每一行就其自身是否满足WHERE条件进行检测,以确定是否排除该行,而不会除去不满足WHERE条件的行衍生出的所有子行,除非这些子行自身就不满足WHERE条件。
图二
步骤五:Oracle将以图二特定的次序返回数据行,用以保证子行显示在父行之下的次序不乱。如图二中,我们可以看到节点圈内标号为2和7的节点是兄弟,但是显示的时候先将2和2的子孙全部显示完毕,再显示7和它的子孙。图二中节点圈内标号1至12是指结点显示的次序,图二中结点之间的结构反映了层次数据节点之间的层次关系。
Oracle中的SELECT语句执行层次查询时,将遵循下列限制:
l SELECT语句执行层次查询时,不能有表之间的JOIN操作。当然,也不可以从一个有JOIN操作的视图中选取数据。
l 如果在SELECT层次查询语句中有ORDER BY从句,Oracle将以ORDER BY的次序重新给选出的层次数据排序,将不再遵循步骤五。
下面我们将讨论START WITH和CONNECT BY从句的使用:
START WITH从句
START WITH从句是用来定位作为层次数据中根数据的行。该从句标示的条件,所有的根行数据必须满足。如果你忽略了该从句,Oracle会将表中所有的行作为根行数据。START WITH中的条件可以包含一个子查询。
CONNECT BY从句
CONNECT BY从句说明了在层次数据中父行和子行之间满足的依据条件。然而,部分条件必须使用PRIOR操作符对应于父行数据,包含PRIOR操作符的条件必须是下列两种表达式中的一个:
PRIOR expr COMPARISON_OPERATOR expr
expr COMPARISON_OPERATOR PRIOR expr
为了查找父行的子行,Oracle将计算表中的每行数据是否满足PRIOR表达式和CONNECT BY中其他的表达式。CONNECT BY可以利用其他的表达式条件,再进一步过滤层次查询中作为子行的行数据。那些能满足CONNECT BY中全部条件的行才会被选择。CONNECT BY从句不允许包含子查询语句。
如果CONNECT BY从句导致返回结果数据是一个循环,Oracle将会返回一个错误。循环发生在一行数据既是另一特定行的祖先行,又是它的子孙行的时刻。也即,层次数据的树形结构被破坏,形成了环。
三、层次查询的使用和探讨
下面我们来举几个例子:
例子一:选择出图一中节点a的所有子孙节点的记录
select * from hierarchical_data_test start with node='a' connect by prior node=parent_node
返回的结果如表二:
显示次序 |
节点 |
父节点 |
1 |
a |
|
2 |
b |
a |
3 |
e |
b |
4 |
k |
e |
5 |
r |
k |
6 |
s |
k |
|