sql之多级菜单设计查询优化

前言

    最近项目在换数据库,从sqlserver转到mysql,目前正在准备阶段,可以预见一些问题。比如mysql中没有一些复杂的函数;这使得需要重写一些数据库语句,甚至要改表结构。
递归树查询是一个其中问题,mysql中没有with..来查询其中子孙节点,所以需要对其进行改造。

继承关系设计(原来设计)

    表结构:

名称 类型 备注
id int PK
parent_id int 父节点id
name varchar(255) 名称

   以下是一些实例数据:


tree-data.png

    我们首先会想到如上的设计" 这样的设计符合第三范式" 能正确的表达菜单的树状结构且没有冗余数据",但在实际开发中这种设计及其不方便,先来看查询一个节点的所有后代,下面是两层的数据的SQL语句:

SELECT t1.*
FROM tree AS t1 LEFT JOIN tree AS t2 
ON t1.id = t2.parent_id
where t2.parent_id=1

    显然,每需要查多一层,就需要联结多一次表。SQL查询的联结次数是有限的,因此不能无限深的获取所有的后代。
    为了实现这样的层次关系的树,就需要多次检索,通过父节点检索出子节点后,再根据检索出的子节点进行检索,以此类推直至到叶子节点,这样的效率对于少量的数据影响还能忍受,但是当树节点扩充到数十条甚至上百条记录后,单单一次菜单显示就要检索数十次该表,整个程序的运行效率就会非常差。
    所以sqlserver2005后就提供了函数with进行递归查询,但是MySQL并没有类似的函数,所以只能自定义函数来实现,或者用一个临时数组或临时表,然后在内存中遍历结果,这样就可以不用检索那么多次数据库了,但这仅仅是把沉重的运算负担从一个地方转移到另一个地方,没有从根本上解决问题。
    下面是用MySQL写的自定义查询所有子节点函数:

CREATE FUNCTION queryChildrenTree(treeId INT)
RETURNS VARCHAR(4000)
BEGIN
DECLARE sTemp VARCHAR(4000);
DECLARE sTempChd VARCHAR(4000);

SET sTemp='$';
SET sTempChd = CAST(treeId AS CHAR);

WHILE sTempChd IS NOT NULL DO
SET sTemp= CONCAT(sTemp,',',sTempChd);
SELECT GROUP_CONCAT(id) INTO sTempChd FROM tree WHERE FIND_IN_SET(parent_id,sTempChd)>0;
END WHILE;
RETURN sTemp;
END;

select * from tree where FIND_IN_SET(parent_id, queryChildrenTree(1))

路径枚举

    路径枚举的设计是指通过将所有祖先的信息联合成一个字符串,并保存为每个节点的一个属性。
    路径枚举是一个由连续的直接层级关系组成的完整路径。如"/home/article/details",其中home是article的直接父亲,这也就意味着home是details的祖先。
   接下来对上面表进行改造:
    表结构(tree1):

名称 类型 备注
id int PK
parent_id int 父节点id
name varchar(255) 名称
path varchar(255) 路径

   以下是一些实例数据:


tree1-data.png

   路径枚举的优点:可以很方便的查询所有的祖先和后代。

--查询所有的子节点(包括孙子)
select * from tree1 where path like '0/1%'
后代
select * from tree1 where '0/1/4/6' like CONCAT(path,'%')
祖先

   路径枚举的缺点也很明显:

  1. 数据库不能确保路径的格式总是正确或者路径中的节点确实存在(中间节点被删除的情况,没外键约束)。
  2. 要依赖高级程序来维护路径中的字符串,并且验证字符串的正确性的开销很大。
  3. VARCHAR的长度很难确定。无论VARCHAR的长度设为多大,都存在不能够无限扩展的情况。

闭包表

   闭包表需要额外创建了一张TreePaths的表(目的:空间换取时间),它记录了表中所有的节点关系,并不仅仅是直接的父子关系。它包含两列,每一列都是一个指向tree3中的treeId的外键。
   这种设计主表已经不再维护节点之间的关系,都是通过外表来维护节点的关系,下面是两个表的结构:
主表结构:

名称 类型 备注
id int PK
name varchar(255) 名称

关系记录表:

名称 类型 备注
id int pk
ancestor int 祖先id
descendant int 后代id
dept int 深度
主表数据:
tree2-data
关联表数据:
treePath-data
查询所有后代节点:
SELECT t.*,tp.dept FROM tree2 AS t
    INNER JOIN TreePath tp on t.id = tp.descendant
    WHERE tp.ancestor = 2

结果如图:


后代
查询所有祖先节点:
SELECT c.* FROM Comment AS c
    INNER JOIN TreePaths t on c.CommentId = t.ancestor
    WHERE t.descendant = 6

结果如图:


祖先
插入数据:

先插入主表的数据,然后增加该节点与其关联的"祖先-后代"关系。

-- 插入主表数据
insert into tree2 values(8,'haha8')
-- 插入关联数据
insert INTO TreePath(ancestor,descendant,dept)
    select t.ancestor,8,(t.dept+1)
    from TreePath AS t
    where descendant = 5
    union all
    SELECT 8,8,0
删除叶子节点:

删除指定节点关联的treePath,然后删除tree数据

DELETE FROM TreePath WHERE descendant = 8
删除子树
DELETE FROM treePath
  where descendant 
  in(SELECT descendant FROM treePath WHERE ancestor = 6)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容