示例

目前有一个功能:任务计划管理,必然存在多级子任务的父子级关系,每个任务还会存在其它数据的关联表。

mysql无法一次性递归查出想要的数据结构,想必很多人都会是通过根目录递归查询数据库的方式查出树结构数据。如果节点数较多,就会造成大量请求Mysql查询,效率会很低。

那么如何优化节点树数据查询效率???

表设计

# task(任务主表) [id,name,...]
[110,"XXX项目排期",...] # task_tree(节点树子任务表) [id,task_id,parent_id,task_name,level,task_num,...]
[1, 110, 110, "XXX项目排期", 0, 0, ...]
[2, 110, 1, "一级主任务1", 1, 1, ...]
[5, 110, 2, "二级子任务1.1", 2, 1, ...]
[6, 110, 2, "二级子任务1.2", 2, 2, ...]
[3, 110, 1, "一级主任务2", 1, 2, ...]
[4, 110, 1, "一级主任务3", 1, 3, ...]
[7, 110, 4, "二级子任务3.1", 2, 1, ...]
[8, 110, 7, "三级子任务3.1.1", 3, 1, ...]
[8, 110, 7, "三级子任务3.1.2", 3, 2, ...] # task_tree_pre(前置任务表) 关联节点任务表 [id,task_id,task_tree_id,val,...]
# 同时关联任务主表的好处是在删除数据的时候减少不必要的关联查询
[1, 110, 6 , "2SF", ...]
[1, 110, 6 , "7SS", ...]

Java使用Map处理

原理:仅发起一次sql请求把所需数据全查出来,使用Map进行逻辑封装处理,效率非常之快。

public class Test {
/**
* 根据主表id查询节点树子任务数据
*/
public List<TaskTree> tree()
{
// 根据主表id查询所有的前置任务
List<TaskTreePre> listPre = "select * from task_tree_pre where task_id = 110";
Map<String, List<TaskTreePre>> mapPre = new HashMap<>();
for (TaskTreePre pre : listPre) {
// 对所有taskTreeId用map封装对应的前置任务列表
List<TaskTreePre> list = new ArrayList<>();
if (mapPre.containsKey(pre.getTaskTreeId())) {
// 先获取到map中已存在的listPre,再继续添加
list = mapPre.get(pre.getTaskTreeId());
}
list.add(pre);
mapPre.put(pre.getTaskTreeId(), list);
}
// 根据主表id查询所有的节点树任务数据
List<TaskTree> listTree = "select * from task_tree where task_id = 110";
Map<String, List<TaskTree>> map = new HashMap<>();
// 设置前置任务
for (TaskTree task : listTree) {
// 根据任务表id获取前置任务并添加
task.setTaskTreePreList(mapPre.get(task.getId()));
// 对所有parentId用map封装所属的子级任务
List<TaskTree> list = new ArrayList<>();
if (map.containsKey(task.getParentId())) {
list = map.get(task.getParentId());
}
list.add(task);
map.put(task.getParentId(), list);
}
// 递归封装节点树结构数据
return initTree("110", map);
} /**
* 递归封装节点树结构数据
* @param map key包含了所有的parentId, value=所属子级列表
* @param id 父级id
* @return
*/
public List<TaskTree> initTree(String parentId, Map<String, List<TaskTree>> map) {
List<TaskTree> list = new ArrayList<>();
if (map.containsKey(parentId)) {
list = map.get(parentId);
if (list.size() > 0) {
for (TaskTree task : list) {
task.setChildren(initTree(map, task.getId()));
}
}
}
return list;
}
}

Java使用Lambda处理

public class Test {
public List<TaskTree> tree(){
// 根据主表id查询所有的节点树任务数据
List<TaskTree> listTree = "select * from task_tree where task_id = 110";
return initTree("110", listTree);
}
/**
* 通过递归算法实现树
* @param parentId 父Id
* @param menuList 当前所有菜单
* @return
*/
private List<TaskTree> initTree(String parentId,List<TaskTree> taskList){
List<TaskTree> list = new ArrayList<>();
/**
* Optional.ofNullable(menuList).orElse(new ArrayList<>()) 如果menuList是空的则返回一个new ArrayList<>()
* .stream() 返回List中的流
* .filter(task -> task.getParentId().equals(parentId)) 筛选List,返回只有条件成立的元素(当前元素的parentId必须等于父id)
* .forEach 遍历这个list
*/
Optional.ofNullable(taskList).orElse(new ArrayList<>())
.stream()
.filter(task -> task.getParentId().equals(parentId))
.forEach(task -> {
task.setChildren(initTree(task.getId(), taskList));
list.add(task);
});
return list;
}
}

Java使用Hutool工具处理

public class Test {
public List<SysTree> list(String id) {
QueryWrapper wrapper = new QueryWrapper();
wrapper.eq("report_id", id);
List<SysTree> list = sysTreeService.list(wrapper);
return treeList("0", list);
} private List<Tree<String>> treeList(String parentId, List<SysTree> list) {
TreeNodeConfig config = new TreeNodeConfig();
List<Tree<String>> treeNodes = TreeUtil.build(
list, parentId, config,
(t, tree) -> {
tree.setId(t.getId());
tree.setName(t.getChapterName());
tree.setParentId(t.getParentId());
// 权重值用于排序
tree.setWeight(Convert.toInt(t.getSortNum()));
// 扩展属性
tree.putExtra("level", t.getLevel());
tree.putExtra("content", t.getContent());
}
);
return treeNodes;
}
}

MySQL 递归查询

SELECT b.id, b.task_id, b.parent_id, b.task_name
FROM(
SELECT @ids AS pids,
(SELECT @ids := GROUP_CONCAT(id) FROM task_tree WHERE task_id = '110' and FIND_IN_SET(parent_id, @ids)) AS cids
FROM task_tree,(SELECT @ids := #{parentId}) c
WHERE @ids IS NOT NULL
) a, task_tree b
WHERE FIND_IN_SET(b.id, a.pids)
ORDER BY b.id

MySQL8 使用WITH RECURSIVE递归查询

MySQL8新增CTE(公共表表达式)写法。比上面的方式更直观、简单易懂。

WITH RECURSIVE  cte_name  AS (
初始语句(非递归部分)
UNION ALL
递归部分语句
)
[ SELECT| INSERT | UPDATE | DELETE] -- WITH RECURSIVE t:t 就是相当于一个临时表的概念
-- WITH AS () 后面必须跟着 [ SELECT| INSERT | UPDATE | DELETE] 语句,否则报错。 -- 示例
WITH RECURSIVE t AS (
SELECT id, name, parent_id FROM task_tree WHERE id = #{parentId}
UNION ALL
SELECT a.id, a.name, a.parent_id FROM task_tree a
JOIN task_tree b ON b.id = a.parent_id
)
SELECT * FROM t;

Java高效率查询Mysql节点树数据的更多相关文章

  1. MySQL_(Java)分页查询MySQL中的数据

    MySQL_(Java)使用JDBC向数据库发起查询请求 传送门 MySQL_(Java)使用JDBC创建用户名和密码校验查询方法 传送门 MySQL_(Java)使用preparestatement ...

  2. php查询mysql返回大量数据结果集导致内存溢出的解决方法

    web开发中如果遇到php查询mysql返回大量数据导致内存溢出.或者内存不够用的情况那就需要看下MySQL C API的关联,那么究竟是什么导致php查询mysql返回大量数据时内存不够用情况? 答 ...

  3. Java 测试Hibernate+Mysql简单的数据存储

    想使用Hibernate框架,在网上看了一个Hibernate学习视频,试着做了一个小小的Java连接数据库的操作,Java初学者一个,大家多多包涵 开发环境: 1.安装MySql, 2.安装了Ecl ...

  4. 树莓派与Arduino Leonardo使用NRF24L01无线模块通信之基于RF24库 (六) 树莓派查询子节点温湿度数据

    nrl24l01每次只能发送4个字节,前面说到,第一个字节用于源节点,第二个字节用于目的节点.因此只剩下两个字节用于温度和湿度,一个字节只有八位,需要表示温湿度的正负数,因此每个字节的第一位表示正负符 ...

  5. python 实现元组中的的数据按照list排序, python查询mysql得到的数据是元组格式,按照list格式对他们排序

    需求: 需要用echart实现软件模块的统计分析,首先是对数据库的数据查询出来,然后给数据封装成列表(list)格式,数据传到前台,在echart实现绑定数据. 因为数据已经按照从大到小的顺序显示出来 ...

  6. 查询mysql所有表数据、字段信息

    根据库名获取所有表的信息 SELECT * FROM information_schema.`TABLES` WHERE TABLE_SCHEMA = 'erp'; 根据库名获取所有表名称和表说明 S ...

  7. mybatis查询mysql的datetime类型数据时间差了14小时

    场景: 数据库字段: mybatis使用 now() 生成时间. 结果: 使用mybatis查询mysql中的数据时,所有时间都比数据库时间多了14小时,考虑了一下,初步判定是系统时区的问题.因为my ...

  8. MySQL实现树状所有子节点查询的方法

    本文实例讲述了MySQL实现树状所有子节点查询的方法.分享给大家供大家参考,具体如下: 在Oracle 中我们知道有一个 Hierarchical Queries 通过CONNECT BY 我们可以方 ...

  9. MySQL知识树-查询语句

    在日常的web应用开发过程中,一般会涉及到数据库方面的操作,其中查询又是占绝大部分的.我们不仅要会写查询,最好能系统的学习下与查询相关的知识点,这篇随笔我们就来一起看看MySQL查询知识相关的树是什么 ...

  10. Mysql对表中 数据 查询的操作 DQL

    准备数据,倒入sql文件 运行sql文件 得到四张表 select * from 表名  * 代表全部 1.AS子句作为别名 select studentname as "姓名" ...

随机推荐

  1. three.js教程1-快速入门

    1.项目开发环境引入threeJs 如果采用的是Vue + threejs或React + threejs技术栈,threejs就是一个js库,直接通过npm命令行安装就行. npm安装特定版本thr ...

  2. navicat premium 15 下载和激活

    Navicat Premium 15 下载地址 链接:https://pan.baidu.com/s/1bL-M3-hkEa4M-547giVjYQ?pwd=1107 推荐安装参考地址:https:/ ...

  3. 不到200行用Vue实现类似Swiper.js的轮播组件

    前言 大家在开发过程中,或多或少都会用到轮播图之类的组件,PC和Mobile上使用 Swiper.js ,小程序上使用swiper组件等. 本文将详细讲解如何用Vue一步步实现的类似Swiper.js ...

  4. mysql通过binlog来恢复被删除的数据库

    binlog日志 查询: MariaDB [(none)]> show variables like 'log_bin'; +---------------+-------+ | Variabl ...

  5. nginx四层负载nginx七层负载,nginx基于nginx-sticky会话保持.

    1. nginx负载均衡实战 nginx提供了 4 7层负载均衡. 可根据业务需求选择不同负载均衡策略. 1.1.1 nginx四层负载均衡[网络层TCP负载] 不支持动静分离,但支持 http my ...

  6. js 检测文本是否溢出

    自定义指令的方式 手写实现 /** * 检测文本是否溢出 * 参考 https://github.com/ElemeFE/element/blob/dev/packages/table/src/tab ...

  7. docker使用Open Policy Agent(OPA)进行访问控制

    目录 一.系统环境 二.前言 三.Open Policy Agent 简介 四.Rego 语言简介 五.配置基本环境 六.docker安装OPA插件 6.1 安装docker 6.2 docker安装 ...

  8. proteus 器件名称被软件篡改bug的解决方案

    proteus v7.8 器件名称被软件篡改bug 的解决方案 BUG描述 在做单片机实验时,发现从某一个时间保存的设计图文件开始,在添加新的电子元件时会出现部分旧元件的名称被捆绑替换为新元件的名称, ...

  9. js布尔类型

        <!-- 禁用 -->     <input type="text" disabled>       <!-- 只读 -->     & ...

  10. SELinux 安全模型——MLS

    首发公号:Rand_cs SELinux 安全模型--MLS BLP 模型:于1973年被提出,是一种模拟军事安全策略的计算机访问控制模型,它是最早也是最常用的一种多级访问控制模型,主要用于保证系统信 ...