热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

树状数据结构存储方式(CUD篇)

在嵌套集合模型中,每个数据其实就是一个节点(node),而每个节点占用2个位值,比如我们先新增一个Smartphones一级节点开始。
前文简单介绍了嵌套集合的数据模型,以及查询的方法,传送门: 树状数据结构存储方式 (查询篇)

Create

在嵌套集合模型中,每个数据其实就是一个节点 (node),而每个节点占用 2 个位值,比如我们先新增一个 Smartphones 一级节点开始。

INSERT INTO `categories` (`title`, `lft`, `rgt`) VALUES('Smartphones', 1, 2);

Smartphones 作为一个主节点 (root),它的 lft 必定为 1,而 rgt 的值,会随着其集合内的子元素增加而增加。

现在,我们希望在 Smartphones 内,添加一个子元素 Android。借助 mysql 的存储过程。

LOCK TABLE categories WRITE;
SELECT @root_left := lft FROM categories WHERE title = 'Smartphones';
UPDATE categories SET rgt = rgt + 2 WHERE rgt > @root_left;
UPDATE categories SET lft = lft + 2 WHERE lft > @root_left;
INSERT INTO categories (title, lft, rgt) VALUES('Android', @root_left + 1, @root_left + 2);
UNLOCK TABLES;
SELECT `title`, `lft`, `rgt` FROM `categories`;
+-------------+-----+-----+
| title       | lft | rgt |
+-------------+-----+-----+
| Smartphones |   1 |   4 |
| Android     |   2 |   3 |
+-------------+-----+-----+

我们再尝试往 Android 内添加一个子元素 小米:

LOCK TABLE categories WRITE;
SELECT @root_left := lft FROM categories WHERE title = 'Android';
UPDATE categories SET rgt = rgt + 2 WHERE rgt > @root_left;
UPDATE categories SET lft = lft + 2 WHERE lft > @root_left;
INSERT INTO categories (title, lft, rgt) VALUES('小米', @root_left + 1, @root_left + 2);
UNLOCK TABLES;
SELECT `title`, `lft`, `rgt` FROM `categories`;
+-------------+-----+-----+
| title       | lft | rgt |
+-------------+-----+-----+
| Smartphones |   1 |   6 |
| Android     |   2 |   5 |
| 小米        |   3 |   4 |
+-------------+-----+-----+

这时候,我们再尝试往 Smartphones 内添加一个子元素 iOS,在前面,我们已经在里面添加了一个 Android 元素,所以这里要调整一下存储过程,将 iOS 插入到 Android 的右边

LOCK TABLE categories WRITE;
SELECT @next_right := rgt FROM categories WHERE title = 'Android';
UPDATE categories SET rgt = rgt + 2 WHERE rgt > @next_right;
UPDATE categories SET lft = lft + 2 WHERE lft > @next_right;
INSERT INTO categories(title, lft, rgt) VALUES('iOS', @next_right + 1, @next_right + 2);
UNLOCK TABLES;
SELECT `title`, `lft`, `rgt` FROM `categories`;
+-------------+-----+-----+
| title       | lft | rgt |
+-------------+-----+-----+
| Smartphones |   1 |   8 |
| Android     |   2 |   5 |
| 小米        |   3 |   4 |
| iOS         |   6 |   7 |
+-------------+-----+-----+

Delete

删除节点时,其实可以看做是新增节点的逆过程,我们引入一个宽度,来衡量节点的宽段,其表示为: rgt - lft + 1 所以我们可以这样写存储过程:

LOCK TABLE categories WRITE;
SELECT @delete_left := lft, @delete_right := rgt, @delete_width := rgt - lft + 1
FROM categories WHERE title = 'Android';
DELETE FROM categories WHERE lft BETWEEN @delete_left AND @delete_right;
UPDATE categories SET rgt = rgt - @delete_width WHERE rgt > @delete_right;
UPDATE categories SET lft = lft - @delete_width WHERE lft > @delete_right;
UNLOCK TABLES;
SELECT `title`, `lft`, `rgt` FROM `categories`;
+-------------+-----+-----+
| title       | lft | rgt |
+-------------+-----+-----+
| Smartphones |   1 |   4 |
| iOS         |   2 |   3 |
+-------------+-----+-----+

Update

移动节点,是一个比较复杂的过程,例如下图,macOS 应该归类到 Unix 分类下。

要实现节点的移动,需要三步:

1、将要移动的节点摘出来

2、重新编排 lft 和 rgt 参数

3、将节点移动到指定位置

LOCK TABLE categories WRITE;
-- 将要移动的节点摘出来,并且重新边篇 lft 和 rgt
SELECT @move_left := lft , @move_right := rgt, @move_width := rgt - lft + 1
FROM categories WHERE title = 'macOS';
UPDATE categories SET rgt = -rgt WHERE lft BETWEEN @move_left AND @move_right;
UPDATE categories SET lft = -lft WHERE lft BETWEEN @move_left AND @move_right;
UPDATE categories SET rgt = rgt - @move_width WHERE rgt > @move_right;
UPDATE categories SET lft = lft - @move_width WHERE lft > @move_right;
-- 将节点放到 Unix 节点里
SELECT @root_left := lft FROM categories WHERE title = 'Unix';
UPDATE categories SET rgt = rgt + @move_width WHERE rgt > @root_left;
UPDATE categories SET lft = lft + @move_width WHERE lft > @root_left;
-- 
UPDATE categories SET lft = @root_left + 1 WHERE lft BETWEEN -@move_right AND -@move_left;
UPDATE categories SET rgt = @root_left + 2 WHERE rgt BETWEEN -@move_right AND -@move_left;
UNLOCK TABLES;

总结

其实 SQL 中的嵌套集合的数据模型已经提出很久了,也有很多包已经实现了这个功能,比如 laravel-nestedset 或者 django-mptt

对于生产使用中,肯定是没有这么简单的表结构设计,或者甚至别的优化,比如一种称为闭合表的数据模型,这个应该会在本系列文章中介绍给大家。

以上就是树状数据结构存储方式(CUD 篇)的详细内容,更多请关注 第一PHP社区 其它相关文章!


推荐阅读
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • Django学习笔记之djangodebugtoolbar使用指南
    介绍django-debug-toolbar是一组可配置的面板,可显示有关当前请求响应的各种调试信息,并在单击时显示有关面板内容的更多详细信息。github地址文档地址安装配置1. ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 本文介绍了django中视图函数的使用方法,包括如何接收Web请求并返回Web响应,以及如何处理GET请求和POST请求。同时还介绍了urls.py和views.py文件的配置方式。 ... [详细]
  • Python项目实战10.2:MySQL读写分离性能优化
    本文介绍了在Python项目实战中进行MySQL读写分离的性能优化,包括主从同步的配置和Django实现,以及在两台centos 7系统上安装和配置MySQL的步骤。同时还介绍了创建从数据库的用户和权限的方法。摘要长度为176字。 ... [详细]
  • macOS命令行创建Android模拟器
    macOS下不安装AndroidStudio使用VSCode来开发Flutter应用使用命令行创建和管理Android模拟器设备avdmanageravdmanager 是一种命令 ... [详细]
  • scrcpy通过adb调试的方式来将手机屏幕投到电脑上,并可以通过电脑控制您的Android设备。它可以通过USB连接,也可以通过Wifi连接(类似于隔空投屏),而且不需要任何ro ... [详细]
author-avatar
爱在明天2502870673
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有