树形结构在关系数据库中的设计

在程序设计中,常常以树形结构表示数据的层次关系,如菜单的结构、商品的分类等。数据库

这样的层次结构在关系数据库中难以直观地表示。常见的一种作法是用一个字段指向上级节点来表示记录的上下级关系。ui

fid pid fname
  1      Food
  2   1    Fruit
  3   2  Red
  4   3  Cherry
  5   2  Yellow
  6   5  Banana 
  7   1  Meat
  8   7  Beef
  9   7  Pork

当要查询某一节点的上一级节点,好比 Beff,能够查询 Beff(pid=7) 指向的那条记录。编码

SELECT * FROM food WHERE fid = 7;

当要查询某一节点的下一级节点,好比 Fruit,能够查询 pid 指向 Fruit(fid=2) 的记录。spa

SELECT * FROM food WHERE pid = 2;

 

另有一种基于左右值编码的设计,这种设计方式的表结构以下:.net

fid fname lft rgt
  1  Food   1    18 
  2  Fruit   2   11 
  3  Red   3    6 
  4    Cherry    4   5 
  5  Yellow   7   10
  6  Banana    8    9 
  7  Meat   12    17  
  8  Beef   13    14 
  9  Pork   15     16  

fid 跟节点的层次彻底没有关系,仅仅用来标识节点。引入了左右值来表示节点之间的关系,以下图所示。设计

这样的设计可以方便地遍历一棵树,从左值数到右值即是先序遍历了一棵(子)树。更多详细的设计,参见 http://www.sitepoint.com/hierarchical-data-database/ 和 http://blog.csdn.net/monkey_d_meng/article/details/66474883d

 

最后一种本身想到的设计:用字符串 str 来标识节点,并约定标识其上级节点的字符串为 substr(str, 1, length(str)-1)。例如,有一节点的以 "abc" 标识,则其上级节点以 "ab" 标识。code

fid fname
 a  Food
 aa  Fruit
 aaa  Red
 aaaa   Cherry
 aab  Yellow
 aaba  Banana 
 ab  Meat
 aba  Beef
 abb  Pork

当要查询某一节点的上一级节点,好比 Beff,由于 Beff 的 fid 为 "aba", 因此其上级节点的 fid 为 "ab"。blog

SELECT * FROM food WHERE fid = 'ab';

当要查询某一节点的路径,好比 Beff,由于 Beff 的 fid 为 "aba",全部其路径为 a/ab/aba。递归

SELECT * FROM food WHERE fid IN ('a', 'ab', 'aba') ORDER BY fid;

当要查询某一节点的下一级节点,好比 Fruit,由于 Fruit 的 fid 为 "aa",因此其下一级节点的 fid 是以 "aa" 开头且比 "aa" 多一个字符的字符串。

SELECT * FROM food WHERE fid LIKE 'aa_';

当要查询某一节点的全部下级节点,好比 Fruit,由于 Fruit 的 fid 为 "aa",因此其全部下级节点的 fid 是以 "aa" 开头的字符串。

SELECT * FROM food WHERE fid LIKE 'aa%' AND fid != 'aa';

 

总结

第一种设计方式, 直观方便,可是在对树的遍历过程当中须要递归查询,数据量大时,对效率的影响很大。这种设计适合的场景:1) 数据量不大的时候; 2) 只会常常查询节点的下一级节点而不会频繁查询节点的全部下级节点。

基于左右值编码的设计方式,消除了遍历树时的递归操做,查询效率高,可是设计较为复杂,增删节点的代价较大。

第三种设计方式,一样删除了遍历树是的递归操做,不管是广度优先搜索(ORDER BY LENGTH(fid))仍是深度优先搜索(ORDER BY fid),都极为方便。这种设计在增删节点时会影响着节点的全部下级节点的标识编码。这种设计方式适合的场景:1) 树形结构基本稳定,不多须要对其增删节点; 2) 须要频繁地查询某个节点的全部下级节点。

相关文章
相关标签/搜索