广告
返回顶部
首页 > 资讯 > 数据库 >MySQL树状结构表查询通解
  • 891
分享到

MySQL树状结构表查询通解

mysql数据库java数据结构 2023-08-19 09:08:01 891人浏览 独家记忆
摘要

文章目录 前言一、数据准备二、代码实现三、案例使用1. 建立数据表实体类2. mapper文件3. 使用 四、总结 前言 ​ 最近做了一个中医药方面的项目,该项目分为游戏端和服务端。笔者负责的是服务端的开发。在服务端的业务中

前言

​ 最近做了一个中医药方面的项目,该项目分为游戏端和服务端。笔者负责的是服务端的开发。在服务端的业务中包含两部分:系统信息管理模块、游戏端服务提供模块。由于中医药存在很多树状结构信息,因此在设计数据表时为了减少冗余度,就将很多数据表设计为了树状结构。树状结构的表能够更加有效的将数据进行管理,但在某些业务中存在查询某个节点所有子节点后父节点的需求,进而造成了查询效率低下。并且对于不同数据表而言,其字段均不相同,代码并不能复用。使得在开发过程中,存在大量重复性工作。笔者想,既然数据表都存在树状结构是否能写一个东西,能够对所有树状结构的数据表都能适用,并提高其查询效率。经过构思找到了一个实现思路。

​ 在很多项目中都存在树状结构的数据库,以表示数据间的关联和层次关系。这种数据库能够高效的描述数据间的关系,而且可以无限延申树状结构的深度。该树状结构的思想能够在数据库层面高效的解决数据存储问题,但在业务处理层面并不能高效的解决节点间父子节点的关系。因此,产生了数据库树状查询问题。对于不同的数据库有不同的解决方式:

  • oracle数据库中存在树查询语句,可以直接查询出某个节点的所有父节点或子节点。

  • Mysql数据库中并没有Oracle数据库中的树查询语句,其解决思路大致可分为两种:

    • 方法一:将数据库中所有的数据一次性全部查询出来,而后在代码层面获取节点的父、子节点
    • 方法二:在代码层面以某个节点的唯一标识在代码层面进行递归,每次递归从数据库中查询树父、子节点

    mysql中方法二由于与数据库进行了多次交互,而与数据库的交互所花费的时间要远远大于代码层面所花费的时间,方法一与数据库只进行一次交互,因此该方法效率比方法二高。

​ 笔者基于方法一对该方法提高了该方法的复用性。方法一在解决树状查询时,通常是仅仅针对特定的一张表而言的,无法通用的解决Mysql树状查询问题,本方法在其基础上通用的解决了MySQL树状查询问题。即是凡是存在树状结构的数据表均可使用本方式进行树状查询。


一、数据准备

CREATE table arborescence (id int PRIMARY KEY,parent_id int,content VARCHAR(200));INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (1, NULL, '节点1');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (2, 1, '节点2');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (3, 1, '节点3');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (4, 2, '节点4');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (5, 2, '节点5');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (6, 2, '节点6');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (7, 3, '节点7');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (8, 3, '节点8');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (9, 3, '节点9');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (10, 4, '节点10');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (11, 6, '节点11');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (12, 7, '节点12');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (13, 8, '节点13');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (14, 8, '节点14');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (15, 9, '节点15');INSERT INTO `arborescence`(`id`, `parent_id`, `content`) VALUES (16, 15, '节点16');

在该数据表中id为每个数据记录的唯一标识,parent_id为每个数据记录的父级记录。

以上数据的树状结构关系如图:

在这里插入图片描述


二、代码实现

在本方法中主要由两个类实现。

public class Tree <T>{    T node;//当前节点    List<Tree<T>> nextNode;//子节点    public Tree(T node, List<Tree<T>> nextNode) {//有参构造        this.node = node;        this.nextNode = nextNode;    }    public Tree() {//无参构造    }    public Tree(T node) {        this.node = node;        this.nextNode=new ArrayList<>();    }    public T getNode() {        return node;    }    public void setNode(T node) {        this.node = node;    }    public List<Tree<T>> getNextNode() {        return nextNode;    }    public void setNextNode(List<Tree<T>> nextNode) {        this.nextNode = nextNode;    }}

​ Tree类与常用的树状节点功能相同,记录当前节点及其直接子节点。主要关键点是使用到了泛型,从而保证了本方法的通用性。

public class TreeUtil <T>{    public List<T> sonList,fatherList;//所有子节点、所有父节点    public Tree<T> tree;//树    public List<T> getSonList() {        return sonList;    }    public void setSonList(List<T> sonList) {        this.sonList = sonList;    }    public List<T> getFatherList() {        return fatherList;    }    public void setFatherList(List<T> fatherList) {        this.fatherList = fatherList;    }    public Tree<T> getTree() {        return tree;    }    public void setTree(Tree<T> tree) {        this.tree = tree;    }    public TreeUtil() {        this.sonList = new ArrayList<>();        this.fatherList=new ArrayList<>();        this.tree=new Tree<>();    }        public void buildListAndTree(List<T> list,Class head,String fatherFiled,String sonFiled,String start) {//根据某个节点建树和集合        Map<String,T> sonMap=new HashMap<>();//以自身唯一标识建map        Map<String,List<T>> fatherMap=new HashMap<>();//以父节点唯一标识建map        for (T temp:list) {            try {                Field field1 = head.getDeclaredField(sonFiled);//自身唯一标识                field1.setAccessible(true);                Object o1 = field1.get(temp);                sonMap.put(o1.toString(),temp);                Field field2 = head.getDeclaredField(fatherFiled);//父节点唯一标识                field2.setAccessible(true);                Object o2 = field2.get(temp);                if (o2==null) continue;//该节点为根节点不存在父节点                if (fatherMap.containsKey(o2.toString())) {//已经含有该父节点                    List<T> ts = fatherMap.get(o2.toString());                    ts.add(temp);                }else {//不含该父节点                    List<T> tempList=new ArrayList<>();                    tempList.add(temp);                    fatherMap.put(o2.toString(),tempList);                }            } catch (Exception e) {                e.printStackTrace();            }        }        if (sonMap.containsKey(start)) {            T startNode = sonMap.get(start);//起始节点            this.sonList = buildSonList(fatherMap,startNode,sonFiled,head);//建子节点            this.fatherList=buildFatherList(sonMap,startNode,head,fatherFiled);//建父节点            this.tree=buildTree(fatherMap,startNode,head,sonFiled);//建树        }    }        private List<T> buildSonList(Map<String,List<T>> fatherMap, T startNode, String sonFiled, Class head){//建集合        List<T> result=new ArrayList<>();        Queue<T> queue=new LinkedList<>();        queue.add(startNode);//队列        while (!queue.isEmpty()) {            T curNode = queue.poll();            try {                Field field = head.getDeclaredField(sonFiled);                field.setAccessible(true);                Object o = field.get(curNode);                if (fatherMap.containsKey(o.toString())) {                    List<T> sons = fatherMap.get(o.toString());//当前节点所有子节点                    queue.addAll(sons);                }                result.add(curNode);            } catch (Exception e) {                e.printStackTrace();            }        }        return result;    }        private List<T> buildFatherList(Map<String,T> sonMap,T startNode,Class head,String fatherFiled){        List<T> result=new ArrayList<>();        try {            Field field = head.getDeclaredField(fatherFiled);            field.setAccessible(true);            while (field.get(startNode)!=null) {                result.add(startNode);                startNode=sonMap.get(field.get(startNode));            }            result.add(startNode);        } catch (Exception e) {            e.printStackTrace();        }        Collections.reverse(result);        return result;    }        private Tree<T> buildTree(Map<String,List<T>> fatherMap,T startNode, Class head,String sonFiled){//建树        try {            Field field = head.getDeclaredField(sonFiled);//当前节点唯一标识            field.setAccessible(true);            Object o = field.get(startNode);            if (fatherMap.containsKey(o.toString())) {//存在子节点                List<T> sons = fatherMap.get(o.toString());                List<Tree<T>> treeSon=new ArrayList<>();                for (T son:sons) {                    Tree<T> temp = buildTree(fatherMap, son, head, sonFiled);                    treeSon.add(temp);                }                Tree<T> root=new Tree<>(startNode,treeSon);                return root;            }else {//不存在子节点                return new Tree<T>(startNode,null);            }        } catch (Exception e) {            e.printStackTrace();        }        return null;    }}

​ TreeUtil类是本方法功能的主要实现类,在使用时只需调用buildListAndTree()方法,需传入5个参数。

  • 第一个参数:数据表中所有有效节点数据
  • 第二个参数:数据表实体类的Class类
  • 第三个参数:实体类中表示父节点字段的属性名(即是上面数据表中parent_id、后续实体类中parentId属性)
  • 第四个参数:实体类中表示当前节点唯一标识字段的属性名(即是上面数据表中id、后续实体类中id属性)
  • 第五个参数:起始节点的唯一标识(即是起始节点的id值)
public void buildListAndTree(List<T> list,Class head,String fatherFiled,String sonFiled,String start) {//根据某个节点建树和集合        Map<String,T> sonMap=new HashMap<>();//以自身唯一标识建map        Map<String,List<T>> fatherMap=new HashMap<>();//以父节点唯一标识建map        for (T temp:list) {            try {                Field field1 = head.getDeclaredField(sonFiled);//自身唯一标识                field1.setAccessible(true);                Object o1 = field1.get(temp);                sonMap.put(o1.toString(),temp);                Field field2 = head.getDeclaredField(fatherFiled);//父节点唯一标识                field2.setAccessible(true);                Object o2 = field2.get(temp);                if (o2==null) continue;//该节点为根节点不存在父节点                if (fatherMap.containsKey(o2.toString())) {//已经含有该父节点                    List<T> ts = fatherMap.get(o2.toString());                    ts.add(temp);                }else {//不含该父节点                    List<T> tempList=new ArrayList<>();                    tempList.add(temp);                    fatherMap.put(o2.toString(),tempList);                }            } catch (Exception e) {                e.printStackTrace();            }        }        if (sonMap.containsKey(start)) {            T startNode = sonMap.get(start);//起始节点            this.sonList = buildSonList(fatherMap,startNode,sonFiled,head);//建子节点            this.fatherList=buildFatherList(sonMap,startNode,head,fatherFiled);//建父节点            this.tree=buildTree(fatherMap,startNode,head,sonFiled);//建树        }    }

三、案例使用

以上面数据库中数据为例,所使用的时mybatis-plus框架

1. 建立数据表实体类

@Data@NoArgsConstructor@AllArgsConstructorpublic class MyNode {    private Integer id;    private Integer parentId;    private String content;}

2. mapper文件

@Mapperpublic interface MyNodeMapper extends BaseMapper<MyNode> {}

3. 使用

@SpringBootTestclass DemoApplicationTests {    @Resource    public MyNodeMapper myNodeMapper;    @Test    void contextLoads() {        List<MyNode> list = myNodeMapper.selectList(null);//查询出数据表中所有有效数据        TreeUtil<MyNode> util1=new TreeUtil<>();        util1.buildListAndTree(list,MyNode.class,"parentId","id","1");//以节点唯一标识为1的节点为起始节点        System.out.println("------------------------以1为起始节点------------------------");        System.out.println("************父节点************");        for (MyNode myNode:util1.getFatherList()) System.out.println(myNode.toString());        System.out.println("************子节点************");        for (MyNode myNode:util1.getSonList()) System.out.println(myNode.toString());        TreeUtil<MyNode> util2=new TreeUtil<>();        util2.buildListAndTree(list,MyNode.class,"parentId","id","3");//以节点唯一标识为3的节点为起始节点        System.out.println("------------------------以3为起始节点------------------------");        System.out.println("************父节点************");        for (MyNode myNode:util2.getFatherList()) System.out.println(myNode.toString());        System.out.println("************子节点************");        for (MyNode myNode:util2.getSonList()) System.out.println(myNode.toString());        TreeUtil<MyNode> util3=new TreeUtil<>();        util3.buildListAndTree(list,MyNode.class,"parentId","id","16");//以节点唯一标识为16的节点为起始节点        System.out.println("------------------------以16为起始节点------------------------");        System.out.println("************父节点************");        for (MyNode myNode:util3.getFatherList()) System.out.println(myNode.toString());        System.out.println("************子节点************");        for (MyNode myNode:util3.getSonList()) System.out.println(myNode.toString());    }}

结果:

------------------------以1为起始节点------------------------************父节点************MyNode(id=1, parentId=null, content=节点1)************子节点************MyNode(id=1, parentId=null, content=节点1)MyNode(id=2, parentId=1, content=节点2)MyNode(id=3, parentId=1, content=节点3)MyNode(id=4, parentId=2, content=节点4)MyNode(id=5, parentId=2, content=节点5)MyNode(id=6, parentId=2, content=节点6)MyNode(id=7, parentId=3, content=节点7)MyNode(id=8, parentId=3, content=节点8)MyNode(id=9, parentId=3, content=节点9)MyNode(id=10, parentId=4, content=节点10)MyNode(id=11, parentId=6, content=节点11)MyNode(id=12, parentId=7, content=节点12)MyNode(id=13, parentId=8, content=节点13)MyNode(id=14, parentId=8, content=节点14)MyNode(id=15, parentId=9, content=节点15)MyNode(id=16, parentId=15, content=节点16)------------------------以3为起始节点------------------------************父节点************MyNode(id=1, parentId=null, content=节点1)MyNode(id=3, parentId=1, content=节点3)************子节点************MyNode(id=3, parentId=1, content=节点3)MyNode(id=7, parentId=3, content=节点7)MyNode(id=8, parentId=3, content=节点8)MyNode(id=9, parentId=3, content=节点9)MyNode(id=12, parentId=7, content=节点12)MyNode(id=13, parentId=8, content=节点13)MyNode(id=14, parentId=8, content=节点14)MyNode(id=15, parentId=9, content=节点15)MyNode(id=16, parentId=15, content=节点16)------------------------以16为起始节点------------------------************父节点************MyNode(id=1, parentId=null, content=节点1)MyNode(id=3, parentId=1, content=节点3)MyNode(id=9, parentId=3, content=节点9)MyNode(id=15, parentId=9, content=节点15)MyNode(id=16, parentId=15, content=节点16)************子节点************MyNode(id=16, parentId=15, content=节点16)

使用本方法后,对于MySQL中所有含有树状结构的表均可直接使用,只需建立对应的实体类,以及明确实体类中表示节点间父子关系的属性名

四、总结

  • 本方法所用到的技术点并不是那么高深,所包含的只是Java的基本内容预计基础的数据结构。如:Java中的泛型、反射,数据结构中的队列、多叉树等基础知识。
    • 泛型的使用使得本方法对任何数据表都具有复用性
    • 反射的使用能够根据动态获取实体类对象的指定属性值
    • 多叉树是本方法中构建树状结构的关键数据结构
    • 在构建树状结构时队列的使用使得构建效率得到了提升
  • 本方法虽然可以解决树状查询的问题,但还存在一些问题,如:在处理大量数据时,存在内存溢出问题
  • 除技术上的总结外,其它非编码能力的提升也很大
    • 在实际开发中要善于发现重复性的工作,并对该工作进行抽象,进而得到一个通用性的解
    • 代码能力需要不断的实践,这个实践并不是说不断做大量重复性的工作,要将所学到的东西付诸实际开发中

来源地址:https://blog.csdn.net/qq_51763048/article/details/129268313

您可能感兴趣的文档:

--结束END--

本文标题: MySQL树状结构表查询通解

本文链接: https://www.lsjlt.com/news/375247.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • MySQL树状结构表查询通解
    文章目录 前言一、数据准备二、代码实现三、案例使用1. 建立数据表实体类2. mapper文件3. 使用 四、总结 前言 ​ 最近做了一个中医药方面的项目,该项目分为游戏端和服务端。笔者负责的是服务端的开发。在服务端的业务中...
    99+
    2023-08-19
    mysql 数据库 java 数据结构
  • MySQL查询树结构方式
    目录MySQL 查询树结构1. 关于树结构2. MySQL自定义函数的方式2.1 创建测试数据2.2 获取 某节点下所有子节点2.3 获取 某节点的所有父节点3. Oracle数据库...
    99+
    2022-11-12
  • MySQL-树型结构数据查询
    建表 SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;-- ------------------------------ Table structure f...
    99+
    2023-09-03
    mysql 数据库 sql
  • MySQL如何实现查询树结构
    这篇文章给大家分享的是有关MySQL如何实现查询树结构的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。MySQL 查询树结构1. 关于树结构此类结构的数据,通常需要表结构中含有id 、parentId等自关联字段,...
    99+
    2023-06-22
  • 一文解析ORACLE树结构查询
    我们在日常程序设计中,经常会遇到树状结构的表示,例如组织机构、行政区划等等。这些在数据库中往往通过一张表进行展示。这里我们以一张简单的行政区划表为例进行展示,在实际使用过程中,可以为其添加其他描述字段以及层级。 表中通...
    99+
    2022-09-02
  • mysql如何查询表结构
    本篇内容主要讲解“mysql如何查询表结构”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“mysql如何查询表结构”吧! mysql查...
    99+
    2022-10-19
  • 华为OD机试 - 树状结构查询(Java & JS & Python)
    题目描述 通常使用多行的节点、父节点表示一棵树,比如 西安 陕西 陕西 中国 江西 中国 中国 亚洲 泰国 亚洲 输入一个节点之后,请打印出来树中他的所有下层节点 输入描述 第一行输入行数,下面是多行数据,每行以空格区分节点和父节点 接着...
    99+
    2023-09-03
    算法 华为机试 Java JavaScript Python
  • oracle表中树形结构与分级查询
    1.此处以emp表为例 此表中 empno 字段与 mgr 字段存在 下级与上级关系 2从上往下查 最顶级的boss 此处的level是三大伪列之一 级别select level...
    99+
    2022-10-18
  • 树形结构的菜单表设计与查询
    开发中经常会遇到树形结构的场景,比如:导航菜单、组织机构等等,但凡是有这种父子层级结构的都是如此,一级类目、二级类目、三级类目。。。 对于这种树形结构的表要如何设计呢?接下来一起探讨一下 首先,想一个问题,用非关系型数据库存储可不可以? ...
    99+
    2016-09-24
    树形结构的菜单表设计与查询
  • SQL Server 通过with as方法查询树型结构
    一、with as 公用表表达式   类似VIEW,但是不并没有创建对象,WITH AS 公用表表达式不创建对象,只能被后随的SELECT语句,其作用:   1. 实现递归查询(树形结构)   2....
    99+
    2022-10-18
  • mysql递归查询树形结构怎么应用
    在MySQL中实现递归查询树形结构可以使用递归查询语句(Recursive Query Statement)或者使用存储过程来实现。...
    99+
    2023-10-25
    mysql
  • MySQL查询树形结构数据的两种方法
    目录1. 递归查询2. 闭包表对于mysql查询树形结构,可以使用递归查询或者闭包表来实现。以下是两种常用的方法: 1. 递归查询 使用递归查询可以遍历树形结构,获取父节点和子节点的关系。假设有一个名为 your_tab...
    99+
    2023-11-11
    MySQL查询树形数据 MySQL查询树形结构 MySQL树形结构查询
  • Sql树结构表怎么查询所有末级节点
    本篇内容主要讲解“Sql树结构表怎么查询所有末级节点”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Sql树结构表怎么查询所有末级节点”吧!目标:显示所有末级节点...
    99+
    2022-10-18
  • SQL Server如何通过with as方法查询树型结构
    SQL Server如何通过with as方法查询树型结构,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。一、with as 公用表表达式 ...
    99+
    2022-10-19
  • MySql树形结构(多级菜单)查询设计方案
    目录背景三级查询(层级固定,层级数少)多级查询(层级不固定/层级很深)遍历整个树:节点搜索(查找出这个节点所在的整个分支)总结背景 又很久没更新了,很幸运地新冠引发了严重的上呼吸道感染,大家羊过后注意休息和防护 工作中(...
    99+
    2023-03-03
    MySql树形结构查询 MySql树形结构
  • MySql树形结构(多级菜单)查询设计方案
    背景 又很久没更新了,很幸运地新冠引发了严重的上呼吸道感染,大家羊过后注意休息和防护 工作中(尤其是传统项目中)经常遇到这种需要,就是树形结构的查询(多级查询),常见的场景有:组织架构(用户部门)查询 和 多级菜单查询 比如,菜单...
    99+
    2023-09-03
    mysql 数据库 java 程序人生 后端
  • mysql的树形结构存储及查询实例分析
    这篇文章主要介绍“mysql的树形结构存储及查询实例分析”,在日常操作中,相信很多人在mysql的树形结构存储及查询实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”mysql的树形结构存储及查询实例分析...
    99+
    2023-06-29
  • mysql通过命令行查看表结构
    mysql通过命令行查看表结构1.语法: DESC 表名;2.语法解析: DESC 表名:列出表的相关信息3.示例: DESC student;4.运行结果截图: ...
    99+
    2022-10-18
  • 详解MySQL子查询(嵌套查询)、联结表、组合查询
    一、子查询 MySQL 4.1版本及以上支持子查询 子查询:嵌套在其他查询中的查询。 子查询的作用: 1、进行过滤: 实例1:检索订购物品TNT2的所有客户的ID = + 一般,在WHERE...
    99+
    2022-10-18
  • mysql查询数据库下表结构的方法
    mysql查询数据库下表结构的方法?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧!方法:1、使用DESCRIBE命令以表格的...
    99+
    2022-10-18
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作