当前位置:网站首页>基于左序遍历的数据存储实践
基于左序遍历的数据存储实践
2022-08-05 02:30:00 【lishuangquan1987】
参考文章
超赞 ! 老外的一种避免递归查询所有子部门的树数据表设计与实现!
但是实践的时候,发现这篇文章有个地方有错误:
它是先更新再删除,发现更新会把要删除节点的节点右值也更新掉。
所以正确的做法是:
删除对应的Sql:
SET @lft := 7;/*要删除的节点左值*/
SET @rgt := 8;/*要删除的节点右值*/
begin;
/*先删除节点*/
DELETE FROM department WHERE lft=@lft AND rgt=@rgt;
/*再更新节点 */
UPDATE department SET lft=lft-2 WHERE lft > @lft;
UPDATE department SET rgt=rgt-2 WHERE rgt > @lft;
/*删除影响行数为0时,必须回滚*/
commit;
/*rollback*/
实践
使用的技术背景
本文实践采用go语言1.18.1
+gormv1.23.6
+sqlitev1.3.5
本次实践是想做一个云配置功能。节点顺序如下:
客户 -> 项目 -> 站别 -> 电脑
客户端展示的效果如下:
实现的目的是按照如上的节点去储存每个电脑的软件配置。
节点模型
type NodeInfo struct {
Id uint `gorm:"primary_key;Column:Id"`
Name string `gorm:"column:Name"`
Level uint `gorm:"column:Level"`
Left uint `gorm:"column:Left"`
Right uint `gorm:"column:Right"`
}
对于Level的定义:
- 1:客户
- 2:项目
- 3:站别
- 4:电脑
查询
客户名称是唯一的,不能重复,其他节点可以重复。
按照客户名称查询客户节点信息:
func getCustomer(customer string) (*models.NodeInfo, error) {
info := &models.NodeInfo{
}
if err := databases.DB.Where("Name=? and Level=1", customer).First(info).Error; err != nil {
return nil, err
} else {
return info, nil
}
}
查询所有的客户节点:只需要查询Level=1
即可
func getCustomers() ([]models.NodeInfo, error) {
infos := make([]models.NodeInfo, 0)
if err := databases.DB.Where("Level=1").Find(&infos).Error; err != nil {
return nil, err
} else {
return infos, nil
}
}
查询指定客户名称下的所有项目节点:
先获取客户节点,拿到客户的左值与右值,然后通过左值与右值获取所有的项目
子节点
func getProjects(customer string) ([]models.NodeInfo, error) {
customerModel := &models.NodeInfo{
}
var err error
if customerModel, err = getCustomer(customer); err != nil {
return nil, err
}
infos := make([]models.NodeInfo, 0)
if err := databases.DB.Where("Level=2 and Left>? and Right<?", customerModel.Left, customerModel.Right).Find(&infos).Error; err != nil {
return nil, err
}
return infos, nil
}
查询站别
、电脑
与以上类似。
插入一个节点
func insertNode(name string, level uint, left uint) (*models.NodeInfo, error) {
model := &models.NodeInfo{
Name: name,
Level: level,
Left: uint(left),
Right: left + 1,
}
if err := databases.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Exec("update NodeInfo set Right=Right+2 where Right>=?", left).Error; err != nil {
return err
}
if err := tx.Exec("update NodeInfo set Left=Left+2 where Left>?", left).Error; err != nil {
return err
}
if err := tx.Create(model).Error; err != nil {
return err
}
return nil
}); err != nil {
return nil, err
}
return model, nil
}
步骤是:先更新其他节点,再插入节点
其中:name
为要插入节点的名称level
为要插入节点的级别,这决定着是插入平行节点还是子节点,left
为要插入节点的左值,通常为父节点
或者左节点
的右值+1
比如一个节点都没有的时候,要插入一个客户名称为AAA
的节点:
insertNode("AAA",1,1)
比如在客户名称
为 AAA,左值为1,右值为2的节点下增加一个项目名称为PPP的节点:
//项目节点:level为2,左值为客户节点“AAA”的右值+1
insertNode("PPP",2,3)
删除一个节点
func removeNode(model *models.NodeInfo) error {
return databases.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Delete(models.NodeInfo{
}, "Left=? and Right=?", model.Left, model.Right).Error; err != nil {
return err
}
if err := tx.Exec("update NodeInfo Set Left=Left-2 where Left>?", model.Left).Error; err != nil {
return err
}
if err := tx.Exec("update NodeInfo set Right=Right-2 where Right>?", model.Right).Error; err != nil {
return err
}
return nil
})
}
步骤是:先删除节点,再更新其他节点,与增加节点操作相反,微信文章上的删除节点是错误的!!!
注意:此函数只能删除没有子节点的单个节点
批量删除节点
func removeNodes(ms []models.NodeInfo) error {
return databases.DB.Transaction(func(tx *gorm.DB) error {
for _, model := range ms {
if err := tx.Delete(models.NodeInfo{
}, "Left=? and Right=?", model.Left, model.Right).Error; err != nil {
return err
}
}
for _, model := range ms {
if err := tx.Exec("update NodeInfo Set Left=Left-2 where Left>?", model.Left).Error; err != nil {
return err
}
if err := tx.Exec("update NodeInfo set Right=Right-2 where Right>?", model.Left).Error; err != nil {
return err
}
}
return nil
})
}
注意:此函数虽然可以批量删除节点,但是要注意使用场景:当选中某个父节点时,父节点下的所有子节点都会被删除,传入时,需要按照Level
字段降序排列传入,否则会出错!
如下图所示:
当删除客户节点时,会查询客户节点下的所有子节点,然后把子节点按照Level
降序排列传入。
删除客户节点的代码如下:
func removeCustomer(customer string) error {
info := &models.NodeInfo{
}
var err error
if info, err = getCustomer(customer); err != nil {
return err
}
//查询customer下的子孙节点,然后全部移除,还要移除文件夹
infos := make([]models.NodeInfo, 0)
if err = databases.DB.Where("Left>=? and Left<=?", info.Left, info.Right).Find(&infos).Error; err != nil {
return err
}
//排序,先移除最下面的节点,再移除上面的节点
linq.From(infos).OrderByDescending(func(i interface{
}) interface{
} {
return i.(models.NodeInfo).Level }).ToSlice(&infos)
if err = removeNodes(infos); err != nil {
return err
}
return nil
}
步骤是:先获取客户节点信息,查询客户节点下的所有子节点(包含自己),然后降序排列节点,调用removeNodes
方法
总结
以上实践,在查询方面确实比传统的通过ParentId来关联好的多,避免了递归查询,但是插入和删除会麻烦一些,各有利弊,但是左序遍历是一种创新的做法,多实践才能感觉到它的好处
边栏推荐
- ARM Mailbox
- How do programmers without objects spend the Chinese Valentine's Day
- 如何模拟后台API调用场景,很细!
- Fragment visibility judgment
- 如何基于OpenVINO POT工具简单实现对模型的量化压缩
- Pisanix v0.2.0 发布|新增动态读写分离支持
- [Unity Entry Plan] Handling of Occlusion Problems in 2D Games & Pseudo Perspective
- 直播回放含 PPT 下载|基于 Flink & DeepRec 构建 Online Deep Learning
- 使用SuperMap iDesktopX数据迁移工具迁移ArcGIS数据
- dotnet 6 为什么网络请求不跟随系统网络代理变化而动态切换代理
猜你喜欢
优化Feed流遭遇拦路虎,是谁帮百度打破了“内存墙”?
2022 EdgeX中国挑战赛8月3日即将盛大开幕
关于#sql shell#的问题,如何解决?
02 [Development Server Resource Module]
RAID disk array
迁移学习——Joint Geometrical and Statistical Alignment for Visual Domain Adaptation
Programmer's list of sheep counting when insomnia | Daily anecdote
2022了你还不会『低代码』?数据科学也能玩转Low-Code啦!
【LeetCode刷题】-数之和专题(待补充更多题目)
How do programmers without objects spend the Chinese Valentine's Day
随机推荐
从零到一快速学会三子棋
Introduction to SDC
Transfer Learning - Joint Geometrical and Statistical Alignment for Visual Domain Adaptation
RAID磁盘阵列
浅谈数据安全治理与隐私计算
dotnet 6 为什么网络请求不跟随系统网络代理变化而动态切换代理
采用redis缓存的linux主从同步服务器图片硬盘满了移到新目录要修改哪些指向
树表的查找
基于OpenVINO工具套件简单实现YOLOv7预训练模型的部署
Intel XDC 2022 Wonderful Review: Build an Open Ecosystem and Unleash the Potential of "Infrastructure"
C学生管理系统 头添加学生节点
02 [Development Server Resource Module]
sql语句多字段多个值如何进行排序
How do programmers without objects spend the Chinese Valentine's Day
C语言实现简单猜数字游戏
Leetcode刷题——22. 括号生成
Live playback including PPT download | Build Online Deep Learning based on Flink & DeepRec
Pisanix v0.2.0 released | Added support for dynamic read-write separation
Simple implementation of YOLOv7 pre-training model deployment based on OpenVINO toolkit
亚马逊云科技携手中科创达为行业客户构建AIoT平台