聊聊mysql的樹形結構存儲及查詢

本文主要研究一下mysql的樹形結構存儲及查詢

存儲parent

這種方式就是每個節(jié)點存儲自己的parent_id信息

  • 建表及數(shù)據(jù)準備
CREATE TABLE `menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `parent_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `menu` (`id`, `name`, `parent_id`) VALUES
(1, 'level1a',  0),
(2, 'level1b', 0),
(3, 'level2a-1a',1),
(4, 'level2b-1a',1),
(5, 'level2a-1b', 2),
(6, 'level2b-1b', 2),
(7, 'level3-2a1a', 3),
(8, 'level3-2b1a', 4),
(9, 'level3-2a1b', 5),
(10, 'level3-2b1b', 6);
  • 查詢
-- 查詢跟節(jié)點下的所有節(jié)點
SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3
FROM menu AS t1
LEFT JOIN menu AS t2 ON t2.parent_id = t1.id
LEFT JOIN menu AS t3 ON t3.parent_id = t2.id
WHERE t1.name = 'level1a';

+---------+------------+-------------+
| lev1    | lev2       | lev3        |
+---------+------------+-------------+
| level1a | level2a-1a | level3-2a1a |
| level1a | level2b-1a | level3-2b1a |
+---------+------------+-------------+

-- 查詢葉子節(jié)點
SELECT t1.name FROM
menu AS t1 LEFT JOIN menu as t2
ON t1.id = t2.parent_id
WHERE t2.id IS NULL;

+-------------+
| name        |
+-------------+
| level3-2a1a |
| level3-2b1a |
| level3-2a1b |
| level3-2b1b |
+-------------+

存儲及修改上比較方便踢京,就是要在sql里頭查詢樹比較費勁,一般是加載到內存由應用自己構造

存儲path

這種方式在存儲parent的基礎上藐石,額外存儲path,即從根節(jié)點到該節(jié)點的路徑

  • 建表及數(shù)據(jù)準備
CREATE TABLE `menu_path` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `parent_id` int(11) NOT NULL DEFAULT '0',
  `path` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `menu_path` (`id`, `name`, `parent_id`, `path`) VALUES
(1, 'level1a', 0, '1/'),
(2, 'level1b', 0, '2/'),
(3, 'level2a-1a',1, '1/3'),
(4, 'level2b-1a',1, '1/4'),
(5, 'level2a-1b', 2, '2/5'),
(6, 'level2b-1b', 2, '2/6'),
(7, 'level3-2a1a', 3, '1/3/7'),
(8, 'level3-2b1a', 4, '1/4/8'),
(9, 'level3-2a1b', 5, '2/5/9'),
(10, 'level3-2b1b', 6, '2/6/10');
  • 查詢
-- 查詢某個節(jié)點的所有子節(jié)點
select * from menu_path where path like '1/%'
+----+-------------+-----------+-------+
| id | name        | parent_id | path  |
+----+-------------+-----------+-------+
| 1  | level1a     | 0         | 1/    |
| 3  | level2a-1a  | 1         | 1/3   |
| 4  | level2b-1a  | 1         | 1/4   |
| 7  | level3-2a1a | 3         | 1/3/7 |
| 8  | level3-2b1a | 4         | 1/4/8 |
+----+-------------+-----------+-------+

查找某個節(jié)點及其子節(jié)點比較方面它褪,就是修改比較費勁旬迹,特別是節(jié)點移動弄贿,所有子節(jié)點的path都得跟著修改

MPTT(Modified Preorder Tree Traversal)

截屏2022-04-04 上午11.58.46.png

不存儲parent_id,改為存儲lft,rgt湿诊,它們的值由樹的先序遍歷順序決定

  • 建表及數(shù)據(jù)準備
CREATE TABLE `menu_preorder` (
  `id` int(11) NOT NULL,
  `name` varchar(50) NOT NULL,
  `lft` int(11) NOT NULL DEFAULT '0',
  `rgt` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

                   1(level1a)14
         2(level2a)7                8(level2b)13
3(level3a-2a)4 5(level3b-2a)6 9(level3c-2b)10 11(level3d-2b)12

INSERT INTO `menu_preorder` (`id`, `name`, `lft`, `rgt`) VALUES
(1, 'level1a', 1, 14),
(2, 'level2a',2, 7),
(3, 'level2b',8, 13),
(4, 'level3a-2a', 3, 4),
(5, 'level3b-2a', 5, 6),
(6, 'level3c-2b', 9, 10),
(7, 'level3d-2b', 11, 12);

select * from menu_preorder
+----+------------+-----+-----+
| id | name       | lft | rgt |
+----+------------+-----+-----+
| 1  | level1a    | 1   | 14  |
| 2  | level2a    | 2   | 7   |
| 3  | level2b    | 8   | 13  |
| 4  | level3a-2a | 3   | 4   |
| 5  | level3b-2a | 5   | 6   |
| 6  | level3c-2b | 9   | 10  |
| 7  | level3d-2b | 11  | 12  |
+----+------------+-----+-----+
  • 查詢
-- 查詢某個節(jié)點及其子節(jié)點狱杰,比如level2b
select * from menu_preorder where lft between 8 and 13
+----+------------+-----+-----+
| id | name       | lft | rgt |
+----+------------+-----+-----+
| 3  | level2b    | 8   | 13  |
| 6  | level3c-2b | 9   | 10  |
| 7  | level3d-2b | 11  | 12  |
+----+------------+-----+-----+

-- 查詢所有葉子節(jié)點
SELECT name
FROM menu_preorder
WHERE rgt = lft + 1;

+------------+
| name       |
+------------+
| level3a-2a |
| level3b-2a |
| level3c-2b |
| level3d-2b |
+------------+

-- 查詢某個節(jié)點及其父節(jié)點
SELECT parent.*
FROM menu_preorder AS node,
menu_preorder AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'level2b'
ORDER BY parent.lft;

+----+---------+-----+-----+
| id | name    | lft | rgt |
+----+---------+-----+-----+
| 1  | level1a | 1   | 14  |
| 3  | level2b | 8   | 13  |
+----+---------+-----+-----+

-- 樹形結構展示
SELECT CONCAT( REPEAT(' ', COUNT(parent.name) - 1), node.name) AS name
FROM menu_preorder AS node,
menu_preorder AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+--------------+
| name         |
+--------------+
| level1a      |
|  level2a     |
|   level3a-2a |
|   level3b-2a |
|  level2b     |
|   level3c-2b |
|   level3d-2b |
+--------------+

好處是通過lft進行范圍(該節(jié)點的lft,rgt作為范圍)查找就可以,缺點就是增刪節(jié)點導致很多節(jié)點的lft及rgt都要修改

小結

  • 存儲parent的方式最為場景厅须,一般樹形結構數(shù)據(jù)量不大的話仿畸,直接在應用層內存構造樹形結構和搜索
  • 存儲path的好處是可以借助path來查找節(jié)點及其子節(jié)點,缺點就是移動node需要級聯(lián)所有子節(jié)點的path朗和,比較費勁
  • MPTT的方式好處是通過lft進行范圍(該節(jié)點的lft,rgt作為范圍)查找就可以错沽,缺點就是增刪節(jié)點導致很多節(jié)點的lft及rgt都要修改

doc

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市眶拉,隨后出現(xiàn)的幾起案子千埃,更是在濱河造成了極大的恐慌,老刑警劉巖忆植,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件放可,死亡現(xiàn)場離奇詭異,居然都是意外死亡朝刊,警方通過查閱死者的電腦和手機耀里,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拾氓,“玉大人冯挎,你說我怎么就攤上這事×埃” “怎么了房官?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長续滋。 經常有香客問我易阳,道長,這世上最難降的妖魔是什么吃粒? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮拒课,結果婚禮上徐勃,老公的妹妹穿的比我還像新娘事示。我一直安慰自己,他們只是感情好僻肖,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布肖爵。 她就那樣靜靜地躺著,像睡著了一般臀脏。 火紅的嫁衣襯著肌膚如雪劝堪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天揉稚,我揣著相機與錄音秒啦,去河邊找鬼。 笑死搀玖,一個胖子當著我的面吹牛余境,可吹牛的內容都是我干的。 我是一名探鬼主播灌诅,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼芳来,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了猜拾?” 一聲冷哼從身側響起即舌,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挎袜,沒想到半個月后顽聂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡宋雏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年芜飘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片磨总。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嗦明,死狀恐怖,靈堂內的尸體忽然破棺而出蚪燕,到底是詐尸還是另有隱情娶牌,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布馆纳,位于F島的核電站诗良,受9級特大地震影響,放射性物質發(fā)生泄漏鲁驶。R本人自食惡果不足惜鉴裹,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧径荔,春花似錦督禽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鹦马,卻和暖如春胧谈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荸频。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工菱肖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人试溯。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓蔑滓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親遇绞。 傳聞我的和親對象是個殘疾皇子键袱,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內容