简介

在开发中经常遇到树形结构的场景,本文将以部门表为例对比几种设计的优缺点;

问题

需求背景:根据部门检索人员,
问题:选择一个顶级部门情况下,跨级展示当前部门以及子部门下的所有人员,表怎么设计更合理 ?

  • 递归吗 ?递归可以解决,但是势必消耗性能

设计1:邻接表

注:(常见父Id设计)

表设计

CREATE TABLE `dept_info01` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',`dept_id` int(10) NOT NULL COMMENT '部门id',`dept_name` varchar(100) NOT NULL COMMENT '部门名称',`dept_parent_id` int(11) NOT NULL COMMENT '父部门id',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

这样是最常见的设计,能正确的表达菜单的树状结构且没有冗余数据,但在跨层级查询需要递归处理。

SQL示例

1.查询某一个节点的直接子集

SELECT * FROM dept_info01  WHERE dept_parent_id =1001

优点

  • 结构简单 ;

缺点

1.不使用递归情况下无法查询某节点所有父级,所有子集

设计2:路径枚举

在设计1基础上新增一个父部门id集字段,用来存储所有父集,多个以固定分隔符分隔,比如逗号。

表设计

CREATE TABLE `dept_info02` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',`dept_id` int(10) NOT NULL COMMENT '部门id',`dept_name` varchar(100) NOT NULL COMMENT '部门名称',`dept_parent_id` int(11) NOT NULL COMMENT '父部门id',`dept_parent_ids` varchar(255) NOT NULL DEFAULT '' COMMENT '父部门id集',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

SQL示例

1.查询所有子集
1).通过模糊查询

SELECT*
FROMdept_info02
WHEREdept_parent_ids like '%1001%'

2).推荐使用 FIND_IN_SET 函数

SELECT*
FROMdept_info02
WHEREFIND_IN_SET( '1001', dept_parent_ids )

优点

  • 方便查询所有的子集 ;
  • 可以因此通过比较字符串dept_parent_ids长度获取当前节点层级 ;

缺点

  • 新增节点时需要将dept_parent_ids字段值处理好 ;
  • dept_parent_ids字段的长度很难确定,无论长度设为多大,都存在不能够无限扩展的情况 ;
  • 节点移动复杂,需要同时变更所有子集中的dept_parent_ids字段值 ;

设计3:闭包表

  • 闭包表是解决分级存储的一个简单而优雅的解决方案,这是一种通过空间换取时间的方式 ;
  • 需要额外创建了一张TreePaths表它记录了树中所有节点间的关系 ;
  • 包含两列,祖先列与后代列,即使这两个节点之间不是直接的父子关系;同时增加一行指向节点自己 ;

表设计

  • 主表
CREATE TABLE `dept_info03` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',`dept_id` int(10) NOT NULL COMMENT '部门id',`dept_name` varchar(100) NOT NULL COMMENT '部门名称',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

  • 祖先后代关系表
CREATE TABLE `dept_tree_path_info` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',`ancestor` int(10) NOT NULL COMMENT '祖先id',`descendant` int(10) NOT NULL COMMENT '后代id',`depth` tinyint(4) NOT NULL DEFAULT '0' COMMENT '层级深度',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

注:depth 层级深度字段 ,自我引用为 1,直接子节点为 2,再一下层为 3,一次类推,第几层就是几 。

SQL示例

插入新节点

INSERT INTO dept_tree_path_info (ancestor, descendant,depth)
SELECT t.ancestor, 3001,t.depth+1 FROM dept_tree_path_info AS t
WHERE t.descendant = 2001
UNION ALL
SELECT 3001,3001,1

查询所有祖先

SELECTc.*
FROMdept_info03 AS c
INNER JOIN dept_tree_path_info t ON c.dept_id = t.ancestor
WHEREt.descendant = 3001

查询所有后代

SELECTc.*
FROMdept_info03 AS c
INNER JOIN dept_tree_path_info t ON c.dept_id = t.descendant
WHERE
t.ancestor = 1001

删除所有子树

DELETE
FROMdept_tree_path_info
WHEREdescendant IN ( SELECTa.dept_id FROM( SELECT descendant dept_id FROM dept_tree_path_info WHERE  ancestor = 1001 ) a)

删除叶子节点

DELETE
FROMdept_tree_path_info
WHEREdescendant = 2001

移动节点

  • 删除所有子树(先断开与原祖先的关系)
  • 建立新的关系

优点

  • 非递归查询减少冗余的计算时间 ;
  • 方便非递归查询任意节点所有的父集 ;
  • 方便查询任意节点所有的子集 ;
  • 可以实现无限层级 ;
  • 支持移动节点 ;

### 缺点

  • 层级太多情况下移动树节点会带来关系表多条操作 ;
  • 需要单独一张表存储对应关系,在新增与编辑节点时操作相对复杂 ;

结合使用

可以将邻接表方式与闭包表方式相结合使用。实际上就是将父id冗余到主表中,在一些只需要查询直接关系的业务中就可以直接查询主表,而不需要关联2张表了。在需要跨级查询时祖先后代关系表就显得尤为重要。

表设计

  • 主表
CREATE TABLE `dept_info04` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',`dept_id` int(10) NOT NULL COMMENT '部门id',`dept_name` varchar(100) NOT NULL COMMENT '部门名称',`dept_parent_id` int(11) NOT NULL COMMENT '父部门id',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  • 祖先后代关系表
CREATE TABLE `dept_tree_path_info` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',`ancestor` int(10) NOT NULL COMMENT '祖先id',`descendant` int(10) NOT NULL COMMENT '后代id',`depth` tinyint(4) NOT NULL DEFAULT '0' COMMENT '层级深度',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

总结

其实,在以往的工作中,曾见过不同类型的设计,邻接表,路径枚举,邻接表路径枚举一起来的都见过。每种设计都各有优劣,如果选择设计依赖于应用程序中哪种操作最需要性能上的优化。

设计 表数量 查询直接子 查询子树 同时查询多个节点子树 插入 删除 移动
邻接表 1 简单 需要递归 需要递归 简单 简单 简单
枚举路径 1 简单 简单 查多次 相对复杂 简单 复杂
闭包表 2 简单 简单 简单 相对复杂 简单 复杂

综上所述

  • 只需要建立子父集关系中可以使用邻接表方式 ;
  • 涉及向上查找,向下查找的需要建议使用闭包表方式 ;

公众号
更多精彩内容、编程故事、心得分享,欢迎关注公众号

树形结构表3种设计优劣分析与分享相关推荐

  1. bom树形结构 表设计_K/3管理视角:树形结构下的BOM管理方式!

    原标题:K/3管理视角:树形结构下的BOM管理方式! BOM(物料清单),也就是以数据格式来描述产品结构的文件,是ERP使用过程中的重要组成部分.通过BOM我们能够清晰的了解产品的结构以及所需要的物料 ...

  2. 多叉树结合JavaScript树形组件实现无限级树形结构(一种构建多级有序树形结构JSON(或XML)数据源的方法)

    一.问题研究的背景和意义 在Web应用程序开发领域,基于Ajax技术的JavaScript树形组件已经被广泛使用,它用来在Html页面上展现具有层次结构的数据项.目前市场上常见的JavaScript框 ...

  3. 扁平和树形结构的几种互转

    后端有时会返回如下扁平数组: [{id: '01',name: '衣服',pid: '0',},{id: '02',name: '裤子',pid: '0',},{id: '03',name: '鞋子' ...

  4. bom树形结构 表设计_BOM模块常用表结构

    表名: bom.bom_bill_of_materials 说明: BOM清单父项目 BILL_SEQUENCE_ID NUMBER 清单序号(关键字) ASSEMBLY_ITEM_ID NUMBER ...

  5. 麻将胡牌算法的一种设计及其分析

    马勇波  陈欣庆 (解放军理工大学工程兵工程学院研究生二队,南京 210007)       摘  要  文章通过一个二维数组定义麻将的数据结构,并在此基础上设计了一种判断麻将是否胡牌的算法,该算法主 ...

  6. oracle 树形结构表,树结构表递归查询在ORACLE和MSSQL中的实现方法

    树结构表:记录树形数据的二维数据表,包含"本节点ID"和"父节点ID" 经常需要进行递归查询某个节点下的所有节点数据 以下是分别在ORACLE和MSSQL中的实 ...

  7. java展示树形结构的两种方式

    1.普通写法[第一个参数是顶级的parentid,第二个参数是数据列表] /*** 递归获取子节点下的子节点* @param integer 父节点的ID* @param treesList 所有菜单 ...

  8. 数据库学习,树形结构的数据库表Schema设计方案

    2019独角兽企业重金招聘Python工程师标准>>> 程序设计过程中,我们常常用树形结构来表征某些数据的关联关系,如企业上下级部门.栏目结构.商品分类等等,通常而言,这些树状结构需 ...

  9. mysql 树形结构_MySQL 树形结构数据库设计 | 剑花烟雨江南

    程序设计过程中,我们常常用树形结构来表示某些数据的关联关系,如企业的部门上下级.电商平台的商品分类等等,通常而言,我们需要通过数据库来完成数据的持久化.由于关系型数据库没有一个很好的树形结构解决方案, ...

最新文章

  1. 计算机基础知识整理大全_【干货整理】高中文言文基础知识大全,速度收藏!...
  2. 正则表达式 右上角加号_最全正则表达式讲解实战,附源码,敲一遍学会
  3. 归纳工作中用到的Linux 命令
  4. windows常用的几个操作
  5. 【Power Automate】如何自动生成Word与PDF文件[上]
  6. 软件工程-c语言--基于at89c51单片机c语言编写的计算器,基于AT89C1单片机C语言编写的计算器.doc...
  7. php 与 memcache 笔记
  8. linux server 5.5下载地址,《红帽Linux 5.5 for x86 服务器版》(RedHat Enterprise Linux Server 5.5 for x86)...
  9. 学习笔记:可持久化线段树(主席树):静态 + 动态
  10. flex 颜色16进制对照表
  11. 机器学习实践笔记(二)EOF
  12. AcWing 91. 最短Hamilton路径(状态压缩DP+哈密顿回路)
  13. 【原】Eclipse部署Maven web项目到tomcat服务器时,没有将lib下的jar复制过去的解决办法...
  14. 基于钉钉小程序做一个记事本
  15. onenote标注pdf笔记_无限接近纸质笔记:学生党的 OneNote 使用心得
  16. django静态页面
  17. Windows调试工具入门-3-WinDbg内核调试配置
  18. PLC培训班一般多少钱?
  19. Java基于内存的消息队列实现
  20. 南京师范大学的计算机科学与技术专业怎么样,南京师范大学计算机科学与技术学院...

热门文章

  1. 运行时绘制Gizmo——关于unity3D的GL图像库的使用(非常详细)
  2. 程序员的核心竞争力是什么?
  3. drupal建站案例_10分钟利用Drupal快速搭建网站
  4. 淘宝直通车的打法的方法与问题分析
  5. C++进阶学习(二)----C到C++II
  6. HTTP流量复制引流工具(web压测及线上问题复现利器)--Gor(GoReplay)
  7. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发机制...
  8. 详细设计说明书(GB8567——88)
  9. 流利阅读 2019.1.5 Gatekeeper at Japan’s ‘Suicide Forest’ hopes music can save lives
  10. 宠物领养系统C语言代码,基于JavaEE的宠物领养系统的设计与实现毕业论文+任务书+中期表+外文翻译及原文+答辩PPT+项目源码及数据库+运行说明...