HTML5技术

数据库表设计,没有最好只有最适合(邻接表、路径枚举、嵌套集、闭包表) - 小小情意(2)

字号+ 作者:H5之家 来源:H5之家 2017-06-08 08:00 我要评论( )

一旦你为每个节点分配了这些数字,就可以使用它们来找到指定节点的祖先和后代。比如搜索评论#4及其所有后代,可以通过搜索哪些节点的ID在评论 #4 的nsleft 和 nsright 范围之间,例: SELECT c2. * FROM comments A

       一旦你为每个节点分配了这些数字,就可以使用它们来找到指定节点的祖先和后代。比如搜索评论#4及其所有后代,可以通过搜索哪些节点的ID在评论 #4 的nsleft 和 nsright 范围之间,例:

SELECT c2.* FROM comments AS c1 JOIN comments AS c2 ON c2.nsleft BETWEEN c1.nsleft AND c1.nsright WHERE c1.comment_id = 4;

比如搜索评论#6及其所有祖先,可以通过搜索#6的ID在哪些节点的nsleft 和 nsright 范围之间,例:

SELECT c2.* FROM comments AS c1 JOIN comments AS c2 ON c1.nsleft BETWEEN c2.nsleft AND c2.nsright WHERE c1.comment_id = 6;



     使用嵌套集设计的主要优势是,当你想要删除一个非叶子节点时,它的后代会自动替代被删除的节点,成为其直接祖先节点的直接后代。就是说已经自动减少了一层。

 

      然而,某些在邻接表的设计中表现得很简单的查询,比如获取一个节点的直接父亲或者直接后代,在嵌套集设计中会变得比较复杂。在嵌套集中,如果需要查询一个节点的直接父亲,我们会这么做,比如要找到评论#6 的直接父亲:

SELECT parent.* FROM comments AS c JOIN comments AS parent ON c.nsleft BETWEEN parent.nsleft AND parent.nsright comments AS in_between ON c.nsleft BETWEEN in_between.nsleft AND in_between.nsright in_between.comment_id IS NULL;

总之有些复杂。

 

    对树进行操作,比如插入和移动节点,使用嵌套集会比其它设计复杂很多。当插入一个新节点时,你需要重新计算新插入节点的相邻兄弟节点、祖先节点和它祖先节点的兄弟,来确保他们的左右值都比这个新节点的左值大。同时,如果这个新节点时一个非叶子节点,你还要检查它的子孙节点。

 

    如果简单快速查询是整个程序中最重要的部分,嵌套集是最好的选择,比操作单独的节点要方便快捷很多。然而,嵌套集的插入和移动节点是比较复杂的,因为需要重新分配左右值,如果你的应用程序需要频繁的插入、删除节点,那么嵌套集可能并不合适。

 

 

三、闭包表

 

     闭包表是解决分级存储的一个简单而优雅的解决方案,它记录了树中所有节点间的关系,而不仅仅只有那些直接的父子节点。

 

     在设计评论系统时,我们额外创建了一个叫 tree_paths 表,它包含两列,每一列都指向 comments 中的外键。

 

     我们不再使用comments 表存储树的结构,而是将树中任何具有(祖先 一 后代)关系的节点对都存储在treepaths 表里,即使这两个节点之间不是直接的父子关系;同时,我们还增加一行指向节点自己。

 

祖先 后代 祖先 后代 祖先 后代 祖先 后代

1 1 1 7 4 6 7 7

1 2 2 2 4 7    

1 3 2 3 5 5    

1 4 3 3 5 6    

1 5 4 4 5 7    

1 6 4 5 6 6    

      通过treepaths 表来获取祖先和后代比使用嵌套集更加的直接。例如要获取评论#4的后代,只需要在 treepaths 表中搜索祖先是评论 #4的行就行了。同样获取后代也是如此。

 

      要插入一个新的叶子节点,比如评论#6的一个子节点,应首先插入一条自己到自己的关系,然后搜索 treepaths 表中后代是评论#6 的节点,增加该节点和新插入节点的“祖先一后代”关系(新节点ID 应该为8):

INSERT INTO treepaths (ancestor, descendant) SELECT t.ancestor, 8 FROM treepaths AS t , 8;

 

要删除一个叶子节点,比如评论#7, 应删除所有treepaths 表中后代为评论 #7 的行:

;

 

要删除一颗完整的子树,比如评论#4 和它所有的后代,可删除所有在 treepaths 表中后代为 #4的行,以及那些以评论#4后代为后代的行。

 

     闭包表的设计比嵌套集更加的直接,两者都能快捷地查询给定节点的祖先和后代,但是闭包表能更加简单地维护分层信息。这两个设计都比使用邻接表或者路径枚举更方便地查询给定节点的直接后代和祖先。

 

     然而你可以优化闭包表来使它更方便地查询直接父亲节点或者子节点: 在 treepaths 表中添加一个 path_length 字段。一个节点的自我引用的path_length 为0,到它直接子节点的path_length 为1,再下一层为2,以此类推。这样查询起来就方便多了。

 

总结:你该使用哪种设计:

     种设计都各有优劣,如何选择设计,依赖于应用程序的哪种操作是你最需要性能上的优化。

 

方案 表数量 查询子 查询树 插入 删除 引用完整性

邻接表 1 简单 困难 简单 简单 是

枚举路径 1 简单 简单 简单 简单 否

嵌套集 1 困难 简单 困难 困难 否

闭包表 2 简单 简单 简单 简单 是

层级数据设计比较

1、邻接表是最方便的设计,并且很多程序员都了解它

2、如果你使用的数据库支持WITH 或者 CONNECT BY PRIOR 的递归查询,那能使得邻接表的查询更高效。

3、枚举路径能够很直观地展示出祖先到后代之间的路径,但同时由于它不能确保引用完整性,使得这个设计非常脆弱。枚举路径也使得数据的存储变得比较冗余。

4、嵌套集是一个聪明的解决方案,但可能过于聪明,它不能确保引用完整性。最好在一个查询性能要求很高而对其他要求一般的场合来使用它。

5、闭包表是最通用的设计,并且以上的方案也只有它能允许一个节点属于多棵树。它要求一张额外的表来存储关系,使用空间换时间的方案减少操作过程中由冗余的计算所造成的消耗。

 

      这几种设计方案只是我们日常设计中的一部分,开发中肯定会遇到更多的选择方案。选择哪一种方案,是需要切合实际,根据自己项目的需求,结合方案的优劣,选择最适合的一种。

 

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • HTML5笔记3——Web Storage和本地数据库 - 邹琼俊

    HTML5笔记3——Web Storage和本地数据库 - 邹琼俊

    2017-06-07 16:00

  • HTML5 进阶系列:indexedDB 数据库 - _林鑫

    HTML5 进阶系列:indexedDB 数据库 - _林鑫

    2017-04-27 14:02

  • 设计模式(1)单例模式(Singleton) - Fonour

    设计模式(1)单例模式(Singleton) - Fonour

    2017-04-23 12:00

  • 云计算之路-阿里云上:数据库连接数过万的真相,从阿里云RDS到微软.NET Core - 博客园团队

    云计算之路-阿里云上:数据库连接数过万的真相,从阿里云RDS到微软.N

    2017-04-08 15:00

网友点评
t