示例

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

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. WordPress函数小结

    1.body_class()函数 为了区分不同的页面,可以用WordPress的body_class()函数 可以在head.php中给body添加:<body <?php body_cl ...

  2. java后台@RequestBody和@RequestParam

    RequestBody 接收的是请求体里面(body)的数据 RequestParam接收的是key-value里面的参数,所以它会被切割进行处理从而可以是普通元素.数组.集合.对象等接收 get-- ...

  3. Web3连接以太网

    1. Infura Infura 是一种托管服务,提供对各种区块链网络的安全可靠访问,消除了管理区块链基础设施的复杂性,使开发者能够专注于构建创新的 Web3 应用程序. Infura 作为连接应用程 ...

  4. Python:用tqdm模块绘制进度条

    在计算密集型迭代计算中,我们常常需要知道当前的迭代轮次,最传统的方法就是打印当前迭代计数器的轮数.那有没有更好的方法呢?我们可以使用tqdm模块(非py内置,需要单独按照)来在控制台绘制进度条,这样更 ...

  5. beego go mod 模式下无法生成注解路由的问题 解决方法

    执行 go get github.com/beego/bee 命令时将bee 命令一定要安装在gopath目录下.有idea或者goland编辑器是最方便的,只需要复制这条命令,然后进入编辑器会提示你 ...

  6. golang开发 gorilla websocket的使用

    很多APP都需要主动向用户推送消息,这就需要用到长连接的服务,即我们通常提到的websocket,同样也是使用socket服务,通信协议是基本类似的,在go中用的最多的.也是最简单的socket服务就 ...

  7. css 文本换行的问题

    今天测试再测试功能的时候无意间发现了这个css的bug,我们可以仔细看第一张图的最后两行,明明还没有到结尾就自动换到最后一行去了,按理说应该是撑满当前行再挤到下一行去,于是我就去看了下css的文本溢出 ...

  8. css 透明气泡效果

    css body { background:#333333; } .login-card { text-align:center; width:430px; height:430px; margin: ...

  9. 数据结构 顺序表(C语言 与 Java实现)以及部分练习题

    目录 数据结构 数组(顺序表) 特点 使用Java实现更高级的数组 C语言实现 总结 优点 缺点 例题 26. 删除有序数组中的重复项 1. 两数之和 27. 移除元素 153. 寻找旋转排序数组中的 ...

  10. 开启安全功能 ES 集群就安全了吗?

    背景 经常跟 ES 打交道的朋友都知道,现在主流的 ES 集群安全方案是:RBAC + TLS for Internal + HTTPS . 作为终端用户一般只需要关心用户名和密码就行了.作为管理和运 ...