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

介绍了一种使用左右值编码实现无限分级树形结构的数据库设计方法,该方法支持高效的非递归查询,以及节点的添加、删除和平移操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                    之前我介绍过一种按位数编码保存树形结构数据的表设计方法,详情见:ITPUB个人空间9],n'I R`b.wN.h
  浅谈数据库设计技巧(上)

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

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

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

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

商品ITPUB个人空间&Kl/O4~@0u vAU
|---食品
o,hu9[$J PZ)/0|    |---肉类
F8Q-_"GdX0|    |    |--猪肉ITPUB个人空间+U lA d1lc~8r
|    |---蔬菜类ITPUB个人空间n{9ERE-`W:kL
|          |--白菜ITPUB个人空间CR!P L(L/e
|---电器ITPUB个人空间^7}n.I&Q%v(i
     |--电视机
$_8I%U-ZP_n%I}m;a0     |--电冰箱

 
采用左右值编码的保存该树的数据记录如下(设表名为 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电器17ITPUB个人空间i;B fN(@8Aie
          +-----------------+                     +---------------------+ITPUB个人空间5`1Em#YG-O:d7nV,`
    3肉类6          7蔬菜类10          13电视机14       15电冰箱16
)_Rr{{2@&y)X0    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
s1g u?L5x0(    ITPUB个人空间8[9? rW@ YU2v:Z
    
@type_id   int
e"nm!?SQs X|x0)ITPUB个人空间8X-vl4g3U%w:b
RETURNS   int ITPUB个人空间J@ W:l e~-euY
AS ITPUB个人空间7^G4zwRU yU0R
begin ITPUB个人空间 KX] @/d*J.?'_
    
declare   @result   int
/oxr O2_B%z0    
set   @result = 0 ITPUB个人空间)Z7b W Rx_
    
declare   @lft   int
&D3J$z9c5N(dh:j sg1E0    
declare   @rgt   int
S'h7r"v%E+pn5A v0    
if   exists  ( select   1   from  tree  where  type_id = @type_id )ITPUB个人空间(WZ-au b7J?e QBg
    
begin
S v0/*EvY'mr0        
select   @lft = lft, @rgt = rgt  from  tree  where  type_id = @type_id
p4]%N/er;m2t0        
select   @result   =   count ( * from  tree  where  lft  <=   @lft   and  rgt  >=   @rgt
z3n6b AB)x&d.c0    
end     
$a)O4o&m1Hf0    
return   @result ITPUB个人空间 YWvE}$w:`
end
+_;R#T0Y/`,@&Bk?"N0
GO ITPUB个人空间Jt)L*e(`cXW N6_{
然后,我们建立如下视图:
CREATE   VIEW  dbo.TreeView
N4[)sj U/g%{Y0
AS ITPUB个人空间y&O+A'qgM c&BW |
SELECT  type_id, name, lft, rgt, dbo.CountLayer(type_id)  AS  layer  FROM  dbo.tree  ORDER   BY  lft
-[k0{j)ZK Z8US0
GO
 
给出对于给定某个节点,对该节点及其子孙节点进行先序遍历的存储过程:
CREATE   PROCEDURE   [ dbo ] . [ GetTreeListByNode ]
q hz"QX{0(ITPUB个人空间c t_$K2p q
    
@type_id   int   -- 给定节点标识
&Q5H6j5Ngfn0
)ITPUB个人空间N6Y9D/q lL
AS
K&I$bv ^:A`"G0
declare   @lft   int
JkQP:O^N"J0
declare   @rgt   int ITPUB个人空间%kk;x-JCP
if   exists  ( select   1   from  tree  where  type_id = @type_id )ITPUB个人空间m m_.v$i9^u pkD
    
begin
+CP'B/C#B0        
select   @lft = lft, @rgt = rgt  from  tree  where  type_id = @type_id ITPUB个人空间RH](UFlGE;U
        
select   *   from  TreeView  where  lft  between   @lft   and   @rgt   order   by  lft  asc ITPUB个人空间`k(U`m y Lz
    
end ITPUB个人空间m Rt0~.E6m2a
go ITPUB个人空间T8Kt,i8r m^
 
现在,我们使用上面的存储过程来列表节点“食品”及其所有子孙节点,查询结果如下:

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+2
4rT3ZuC0          +-----------------+                                    +-------------------------+ITPUB个人空间&ZJu?#r5ZE0[
    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 ]
"/u3bvc ?hS$C1D g0(
3i.Dn.l1ro D8YG0    
@type_id   int ,ITPUB个人空间 |:sg[;T
    
@name   varchar ( 50 )ITPUB个人空间 [X6D `Y+A
)ITPUB个人空间.J| i(OaS
AS
|$i.xc&O,X{0
declare   @rgt   int
d Z G[l{,l+i0
if   exists  ( select   1   from  tree  where  type_id = @type_id )ITPUB个人空间 `/*iqk[9/ i't
    
begin ITPUB个人空间axT x'|eV%[D
       
SET  XACT_ABORT  ON
rf Xc7Br"ntM9i0       
BEGIN   TRANSACTION ITPUB个人空间X `.O^rc
        
select   @rgt = rgt  from  tree  where  type_id = @type_id ITPUB个人空间&w/mughF
        
update  tree  set  rgt = rgt + 2   where  rgt >= @rgt
Hq7M },U H'j(ZS0        
update  tree  set  lft = lft + 2   where  lft >= @rgt
$k/C*n@?B0        
insert   into  tree (name,lft,rgt)  values  ( @name , @rgt , @rgt + 1 )    
sT0X7D:xI3h /-t0        
COMMIT   TRANSACTION
#K8x9nt0~0       
SET  XACT_ABORT  OFF     ITPUB个人空间+A9{ giSGa-D/Z8Z
    
end ITPUB个人空间+D+w O7i#b7ve
go
+a](Nf7b"k+G0
 
然后,我们删除节点“电视机”,再来看看该树会变成什么情况:
                                            1商品20-2
                       +-----------------------------------+
                2食品13                                14电器19-2
9^ L6l"s+onl0          +-----------------+               
"`N|}OX'YTI/&IgT0       3肉类8          9蔬菜类12              17-2电冰箱18-2ITPUB个人空间/dP8`Y/B:s
    +----------+
4猪肉5  6牛肉7  10白菜11
 
  相应的存储过程如下:
CREATE   PROCEDURE   [ dbo ] . [ DelNode ]  
-z@,Bh_Y+{ }0    
@type_id   int
q~?!EEOv0
AS ITPUB个人空间Fv5CJ Og
declare   @lft   int ITPUB个人空间D+Lh[[ni5m
declare   @rgt   int
4C WS ]#R@$/5S ]%Q0
if   exists  ( select   1   from  tree  where  type_id = @type_id )ITPUB个人空间O-J-pG5JU.k%@
    
begin ITPUB个人空间:H/2v_ w+R:ty/+V
       
SET  XACT_ABORT  ON
JY*v)Y?!N*p t5f0       
BEGIN   TRANSACTION ITPUB个人空间k_!/#@t"o(i / u
        
select   @lft = lft, @rgt = rgt  from  tree  where  type_id = @type_id
k7OM;@lF(}0        
delete   from  tree  where  lft >= @lft   and  rgt <= @rgt
8_&~ TD is5W0        
update  tree  set  lft = lft - ( @rgt - @lft + 1 where  lft > @lft ITPUB个人空间"Y,|,d2NC.Gu:H^U]
<IMG alt="" src="http://images.youkuaiyun.com/syntaxhighlighting/OutliningIndi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值