设计模式体现的是一种思想,而思想是指导行为的一切------------开发智能要比机械性的生产商品要简单的多./我要从南走到北,从1到2^10/-----------------Life is a pure flame, and we live by an invisible within us.

12.采用左右值编码来存储无限分级树形结构的数据库表设计[摘自网络]

上一篇 / 下一篇  2008-05-05 10:42:03 / 个人分类:::方法类::

   摘子:http://blog.csdn.net/comiunknown/archive/2007/04/26/1586020.aspx

        之前我介绍过一种按位数编码保存树形结构数据的表设计方法,详情见:
;dkrj7@ a8U2G9T0  浅谈数据库设计技巧(上)

  该设计方案的优点是:只用一条查询语句即可得到某个根节点及其所有子孙节点的先序遍历。由于消除了递归,在数据记录量较大时,可以大大提高列表效率。但是,这种编码方案由于层信息位数的限制,限制了每层能所允许的最大子节点数量及最大层数。同时,在添加新节点的时候必须先计算新节点的位置是否超过最大限制。

  上面的设计方案必须预先设定类别树的最大层数以及最大子节点数,不是无限分级,在某些场合并不能采用,那么还有更完美的解决方案吗?通过 google的搜索,我又探索到一种全新的无递归查询,无限分级的编码方案——左右值。原文的程序代码是用php写的,但是通过仔细阅读其数据库表设计说明及相关的sql语句,我彻底弄懂了这种巧妙的设计思路,并在这种设计中新增了删除节点,同层平移的需求(原文只提供了列表及插入子节点的sql语句)。

  下面我力图用比较简短的文字,少量图表,及相关核心sql语句来描述这种设计方案:

  首先,我们弄一棵树作为例子:

商品
xG6l+d K;D;q^2i0|---食品ITPUB个人空间}L9H5if;s$@
|    |---肉类
L2q5WTDLL|0|    |    |--猪肉ITPUB个人空间"m4z n:C/S
|    |---蔬菜类
h1VbB`%j0|          |--白菜
u;ZF*~Ku0|---电器ITPUB个人空间0]E_7Adz
     |--电视机
!Z.DR+PQ5dA H0     |--电冰箱

 
采用左右值编码的保存该树的数据记录如下(设表名为tree):
Type_id
Name
Lft
Rgt
1
商品
1
18
2
食品
2
11
3
肉类
3
6
4
猪肉
4
5
5
蔬菜类
7
10
6
白菜
8
9
7
电器
12
17
8
电视机
13
14
9
电冰箱
15
16
 
第一次看见上面的数据记录,相信大部分人都不清楚左值(Lft)和右值(Rgt)是根据什么规则计算出来的,而且,这种表设计似乎没有保存父节点的信息。下面把左右值和树结合起来,请看:
          1商品18
     +---------------------------------------+
                2食品11                                    12电器17
SkOM j4y}i8I0          +-----------------+                     +---------------------+
-rJ xfLmJ b AW0    3肉类6          7蔬菜类10          13电视机14       15电冰箱16
~h7xI?4I^v7~0    4猪肉5           8白菜9
请用手指指着上图中的数字,从1数到18,学习过数据结构的朋友肯定会发现什么吧?对,你手指移动的顺序就是对这棵树的进行先序遍历的顺序。接下来,让我讲述一下如何利用节点的左右值,得到该节点的父节点,子孙节点数量,及自己在树中的层数。
 
假定我们要对节点“食品”及其子孙节点进行先序遍历的列表,只需使用如下一条sql语句:
select * from tree where Lft between 2 and 11 order by Lft asc
查询结果如下:
Type_id
Name
Lft
Rgt
2
食品
2
11
3
肉类
3
6
4
猪肉
4
5
5
蔬菜类
7
10
6
白菜
8
9
 
那么某个节点到底有多少子孙节点呢?很简单,子孙总数 =(右值-左值-1)/2 
以节点“食品”举例,其子孙总数=(11-2-1)/ 2 = 4
 
同时,我们在列表显示整个类别树的时候,为了方便用户直观的看到树的层次,一般会根据节点所处的层数来进行相应的缩进,那么,如何计算节点在树中的层数呢?还是只需通过左右值的查询即可,以节点“食品”举例,sql语句如下:
select count(*) from tree where lft <= 2 and rgt >= 11
为了方便列表,我们可以为tree表建立一个视图,添加一个层数列,该类别的层数可以写一个自定义函数来计算。该函数如下:
CREATE FUNCTION dbo.CountLayer
IS'b}s1N|]0(    
u7b9wnF|^ p0    
@type_id int
-a;k3pV0r"xAp0)ITPUB个人空间o)r,`)GL_{4|
RETURNS intITPUB个人空间4Y jq1m:N#ZKU
ASITPUB个人空间+\3E,d)[6z!lB+h
beginITPUB个人空间gU?`mVT }
    
declare @result int
2b~ i%^;T:w D l-Z0    
set @result=0
_x G Y3f5j5A_0    
declare @lft int
+x XK3[wW%dW5kr0    
declare @rgt intITPUB个人空间A*CLpO!SO
    
if exists (select 1 from tree where type_id=@type_id)
G9yf9bE0S0    
beginITPUB个人空间5`zWp'HR"J
        
select @lft=lft,@rgt=rgt from tree where type_id=@type_idITPUB个人空间9BT-P+~9lk'D/Q%T
        
select @result = count(*from tree where lft <= @lft and rgt >= @rgt
gL'V}v2mq ~0    
end    
+Hu s!A'Z4Vb0    
return @result
9?F6["@8o.KF8NPP0
endITPUB个人空间H LQb(L7R2e
GOITPUB个人空间I"xPF#b$Pd
然后,我们建立如下视图:
CREATE VIEW dbo.TreeViewITPUB个人空间R*r f$d.t]_&J
ASITPUB个人空间[?cw)y+T&V
SELECT type_id, name, lft, rgt, dbo.CountLayer(type_id) AS layer FROM dbo.tree ORDER BY lft
#@.@N!b%x DF:E#Fh{ Y0
GO
 
给出对于给定某个节点,对该节点及其子孙节点进行先序遍历的存储过程:
CREATE PROCEDURE [dbo].[GetTreeListByNode]
Io/Fqhxx0(
`?^Ew8oc0    
@type_id int --给定节点标识ITPUB个人空间 v^d:t6cpOUg
)
&qHCvc/T.yb0
ASITPUB个人空间1V&[ An`i
declare @lft int
_ZR3w1T'P1^0
declare @rgt int
7l\~RR6O s)q0
if exists (select 1 from tree where type_id=@type_id)
yz.V:s9Y:@(V0    
beginITPUB个人空间k+[J8m-MUQv?
        
select @lft=lft,@rgt=rgt from tree where type_id=@type_id
,i u&wV)`1t T0        
select * from TreeView where lft between @lft and @rgt order by lft ascITPUB个人空间JDI2x{#h0o
    
endITPUB个人空间,JnB Ul;OQ?
go
6A S9ZGqv\ g0
 
现在,我们使用上面的存储过程来列表节点“食品”及其所有子孙节点,查询结果如下:
Type_id
Name
Lft
Rgt
Layer
2
食品
2
11
2
3
肉类
3
6
3
4
猪肉
4
5
4
5
蔬菜类
7
10
3
6
白菜
8
9
4
 
 
采用左右值编码的设计方案,在进行类别树的遍历时,由于只需进行2次查询,消除了递归,再加上查询条件都为数字比较,效率极高,类别树的记录条目越多,执行效率越高。看到这里,相信不少人对这种设计方案有所心动了,下面让我们接着看看如何在这种表结构中实现插入、删除、同层平移节点(变更同层节点排序)的功能。
 
假定我们要在节点“肉类”下添加一个子节点“牛肉”,该树将变成:
                                     1商品18+2
                      +--------------------------------------------+
                2食品11+2                                  12+2电器17+2ITPUB个人空间 L3ZF:nR
          +-----------------+                                    +-------------------------+ITPUB个人空间Lr$D|9kO6v
    3肉类6+2   7+2蔬菜类10+2         13+2电视机14+2    15+2电冰箱16+2
    +-------------+ 
4猪肉5 6牛肉7  8+2白菜9+2
 
看完上图相应节点左右值的变化后,相信大家都知道该如何写相应的sql脚本吧?下面我给出相对完整的插入子节点的存储过程:
CREATE PROCEDURE [dbo].[AddSubNodeByNode]
wTn8t_g}?J)L3n0(
!F$Y0PjhH3m)@/~0    
@type_id int,
(K(`^ zp8J3Z0    
@name varchar(50)
qcwQl5k F%C%G0)
1f?/J$]9\)C0
AS
6i7w~&[uN5_0
declare @rgt intITPUB个人空间D1v+tda7@/a$n[5p
if exists (select 1 from tree where type_id=@type_id)
3h8O? [6kNU0Y~q0    
beginITPUB个人空间|2IJ9s B'MB bu"g
       
SET XACT_ABORT ON
!y]"_\_ e,vF0       
BEGIN TRANSACTION
'ic-I\_Y Xr6@Q_ V0        
select @rgt=rgt from tree where type_id=@type_id
C^(vJ4?HSA0        
update tree set rgt=rgt+2 where rgt>=@rgtITPUB个人空间M'c E&tyy~
        
update tree set lft=lft+2 where lft>=@rgt
s4Y#He@,X!FI lta V0        
insert into tree (name,lft,rgt) values (@name,@rgt,@rgt+1)    
d,K;])c l'A0        
COMMIT TRANSACTION
'@'PtuS0       
SET XACT_ABORT OFF    
sk Q/_U9n8C4X0    
end
i0z7\)s3Zh!CF0
go
`/w K%r b3Tg"U0
 
然后,我们删除节点“电视机”,再来看看该树会变成什么情况:
                                            1商品20-2
                       +-----------------------------------+
                2食品13                                14电器19-2
bj{}3jk |+K;f Lu|0          +-----------------+               
D"]*O/`g$rK$Jr|0       3肉类8          9蔬菜类12              17-2电冰箱18-2ITPUB个人空间5v lO @`VJ&x,t
    +----------+
4猪肉5  6牛肉7  10白菜11
 
  相应的存储过程如下:
CREATE PROCEDURE [dbo].[DelNode] 
+B|(Ib6TfIR0    
@type_id int
Q7C3x*Z#oy.o0
ASITPUB个人空间t bT+u:jU+B UZ
declare @lft int
d"u6WV H7@ Q0
declare @rgt intITPUB个人空间BCO&u4zO[
if exists (select 1 from tree where type_id=@type_id)
HQ5QU2w-I&K N0    
beginITPUB个人空间R&}3yi wst
       
SET XACT_ABORT ONITPUB个人空间zRKy+M4?{] A,}2jl
       
BEGIN TRANSACTIONITPUB个人空间/b'z3J JXn/IIR
        
select @lft=lft,@rgt=rgt from tree where type_id=@type_id
mOc&e0s Q0        
delete from tree where lft>=@lft and rgt<=@rgtITPUB个人空间a+AZ$W~~({ }2Vf
        
update tree set lft=lft-(@rgt-@lft+1where lft>@lft
7F M/J {4g$Ap;l0<IMG alt="" src="http://images.csdn.net/syntaxhighlighting/OutliningIndi

TAG:

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

日历

« 2008-07-07  
  12345
6789101112
13141516171819
20212223242526
2728293031  

数据统计

  • 访问量: 202
  • 日志数: 17
  • 建立时间: 2008-04-10
  • 更新时间: 2008-05-14

RSS订阅