Adjacent List Model 與 Nested Set Model 兩種無限分類模型的對比

原文連接:www.pilishen.com/posts/Adjac…; 歡迎做客咱們的php&Laravel學習羣:109256050php

此文檔是 nestedset-無限分類正確姿式的擴展閱讀node

咱們都曾在數據庫中處理過層級數據-這種數據中的每項都有一個父項和(0或多個)子項,根項除外。好比:論壇和郵件列表中的分類、商業組織結構表、內容管理系統的分類和產品分類等等。 在關係型數據庫中處理層級數據時咱們總會以爲關係型數據庫不是爲處理層級數據設計的,由於關係型數據庫的數據表不像XML具備層級,而是一個簡單的扁平化的表。因此層級數據的這種父-子關係不能在數據表中天然的展示出來。數據庫

下面咱們介紹兩種在關係型數據庫中處理層級數據的模型:bash

Adjacent List Model 鄰接表模型

咱們如下圖的電子產品分類爲例electron

圖片.png

一般上面的產品分類會像下面這樣來設計表結構並被儲存:函數

CREATE TABLE category(
        category_id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(20) NOT NULL,
        parent INT DEFAULT NULL
);

INSERT INTO category VALUES(1,'ELECTRONICS',NULL),(2,'TELEVISIONS',1),(3,'TUBE',2),
        (4,'LCD',2),(5,'PLASMA',2),(6,'PORTABLE ELECTRONICS',1),(7,'MP3 PLAYERS',6),(8,'FLASH',7),
        (9,'CD PLAYERS',6),(10,'2 WAY RADIOS',6);

SELECT * FROM category ORDER BY category_id;
+-------------+----------------------+--------+
| category_id | name                 | parent |
+-------------+----------------------+--------+
|           1 | ELECTRONICS          |   NULL |
|           2 | TELEVISIONS          |      1 |
|           3 | TUBE                 |      2 |
|           4 | LCD                  |      2 |
|           5 | PLASMA               |      2 |
|           6 | PORTABLE ELECTRONICS |      1 |
|           7 | MP3 PLAYERS          |      6 |
|           8 | FLASH                |      7 |
|           9 | CD PLAYERS           |      6 |
|          10 | 2 WAY RADIOS         |      6 |
+-------------+----------------------+--------+
10 rows in set (0.00 sec)
複製代碼

在鄰接表模型中,表中的每條記錄都包含一個指向父項id的字段。根項也就是這裏的electronics的父項id爲null,這種表結構很是的簡單,並且咱們很清楚的看到:flash的父項是MP3 , MP3的父項是protable electronics、protable electronics 的父項是electronics等等。雖然在客戶端編碼中鄰接表模型處理起來也至關的簡單,可是若是是純SQL編碼的話,該模型會有不少問題。post

檢索整個樹

檢索整個樹是處理層級數據最多見的任務,爲了完成這個最經常使用的方法是經過自鏈接(self-join):性能

SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS';

+-------------+----------------------+--------------+-------+
| lev1        | lev2                 | lev3         | lev4  |
+-------------+----------------------+--------------+-------+
| ELECTRONICS | TELEVISIONS          | TUBE         | NULL  |
| ELECTRONICS | TELEVISIONS          | LCD          | NULL  |
| ELECTRONICS | TELEVISIONS          | PLASMA       | NULL  |
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS  | FLASH |
| ELECTRONICS | PORTABLE ELECTRONICS | CD PLAYERS   | NULL  |
| ELECTRONICS | PORTABLE ELECTRONICS | 2 WAY RADIOS | NULL  |
+-------------+----------------------+--------------+-------+
6 rows in set (0.00 sec)
複製代碼

檢索全部的葉子節點

咱們能夠經過左鏈接(left-join)檢索出全部的葉子節點(沒有子節點的節點):學習

SELECT t1.name FROM
category AS t1 LEFT JOIN category as t2
ON t1.category_id = t2.parent
WHERE t2.category_id IS NULL;

+--------------+
| name         |
+--------------+
| TUBE         |
| LCD          |
| PLASMA       |
| FLASH        |
| CD PLAYERS   |
| 2 WAY RADIOS |
+--------------+
複製代碼

檢索一條路徑

自鏈接一樣可讓我檢索出層級結構中的一條完整的層級路徑網站

SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS' AND t4.name = 'FLASH';

+-------------+----------------------+-------------+-------+
| lev1        | lev2                 | lev3        | lev4  |
+-------------+----------------------+-------------+-------+
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS | FLASH |
+-------------+----------------------+-------------+-------+
1 row in set (0.01 sec)
複製代碼

這個方法的主要侷限性在於對於層級結構中的每一層都要自鏈接,而且隨着層級的增長這種自鏈接將變的愈來愈複雜也就天然的影響着性能。

鄰接表模型的侷限性

在純SQL中使用鄰接表模型會很困難,若是想檢索某個分類在層級結構中的路徑就須要事先知道它在層級結構中所處的層級,此外當進行刪除某項操做時須要很是當心,由於在刪除的過程當中可能會致使出現整個的孤立的子樹(刪除portable electronics 會使它下面全部的子項變孤立)。其中一些侷限能夠經過使用客戶端編碼或Stored Procedure(存儲過程是一組爲了完成特定功能的SQL語句集,經編譯後存儲在數據庫中,用戶經過指定存儲過程的名字並給定參數來調用執行它)來解決。使用面向過程的語言,咱們能夠從樹的底部開始,向上迭代返回完整的樹或一條路徑。咱們也能夠經過提高子項和對剩下的子項從新排序以使之指向新的父項來避免產生孤立的子樹。

The Nested Set Model 嵌套集合模型

本篇文章將聚焦於嵌套集合模型,在這種模型中咱們能夠以一種新的方式來看待咱們的層級數據,不是以節點和節點之間的線,而是以一種嵌套容器的方式,試着如下面的方式來展現咱們的電器分類:

圖片.png

注意咱們是怎麼實現層級結構的,父項包含它們全部的子節點。咱們經過使用左值和右值來在數據表上表示這種形式的層級結構,以此表示項目的嵌套關係

CREATE TABLE nested_category (
        category_id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(20) NOT NULL,
        lft INT NOT NULL,
        rgt INT NOT NULL
);

INSERT INTO nested_category VALUES(1,'ELECTRONICS',1,20),(2,'TELEVISIONS',2,9),(3,'TUBE',3,4),
 (4,'LCD',5,6),(5,'PLASMA',7,8),(6,'PORTABLE ELECTRONICS',10,19),(7,'MP3 PLAYERS',11,14),(8,'FLASH',12,13),
 (9,'CD PLAYERS',15,16),(10,'2 WAY RADIOS',17,18);

SELECT * FROM nested_category ORDER BY category_id;

+-------------+----------------------+-----+-----+
| category_id | name                 | lft | rgt |
+-------------+----------------------+-----+-----+
|           1 | ELECTRONICS          |   1 |  20 |
|           2 | TELEVISIONS          |   2 |   9 |
|           3 | TUBE                 |   3 |   4 |
|           4 | LCD                  |   5 |   6 |
|           5 | PLASMA               |   7 |   8 |
|           6 | PORTABLE ELECTRONICS |  10 |  19 |
|           7 | MP3 PLAYERS          |  11 |  14 |
|           8 | FLASH                |  12 |  13 |
|           9 | CD PLAYERS           |  15 |  16 |
|          10 | 2 WAY RADIOS         |  17 |  18 |
+-------------+----------------------+-----+-----+
複製代碼

咱們使用lft和rgt來表示左值和右值,然而咱們怎麼肯定這兩個值呢?咱們從外出節點最左邊向右邊編號:

圖片.png

這種設計也能夠運用在經典樹上:

圖片.png

檢索整個樹

因爲一個節點的左值永遠在其父節點的左值和右值之間,因此咱們能夠經過自鏈接來檢索一整個樹:

SELECT node.name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND parent.name = 'ELECTRONICS'
ORDER BY node.lft;

+----------------------+
| name                 |
+----------------------+
| ELECTRONICS          |
| TELEVISIONS          |
| TUBE                 |
| LCD                  |
| PLASMA               |
| PORTABLE ELECTRONICS |
| MP3 PLAYERS          |
| FLASH                |
| CD PLAYERS           |
| 2 WAY RADIOS         |
+----------------------+
複製代碼

不想上面鄰接表模型中的例子,這種查詢不須要知道樹的深度,咱們也不須要在乎是否須要在BETWEEN語句中加上節點右值的判斷語句,由於右值老是與左值處於相同的父項下。

檢索全部的葉子節點

在嵌套集合模型中檢索葉子節點也比在鄰接表模型中使用左鏈接方法要簡單得多。若是你仔細觀察nested_category表,你會發現全部的葉子節點的左值與右值是兩個連續的數字,全部爲了檢索葉子節點,咱們只須要檢索那些rgt = lft +1 的節點就好了:

SELECT name
FROM nested_category
WHERE rgt = lft + 1;

+--------------+
| name         |
+--------------+
| TUBE         |
| LCD          |
| PLASMA       |
| FLASH        |
| CD PLAYERS   |
| 2 WAY RADIOS |
+--------------+
複製代碼

檢索一條路徑

使用嵌套集合模型,咱們能夠檢索一條路徑而不須要以前那樣複雜的屢次自鏈接:

SELECT parent.name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.name = 'FLASH'
ORDER BY parent.lft;

+----------------------+
| name                 |
+----------------------+
| ELECTRONICS          |
| PORTABLE ELECTRONICS |
| MP3 PLAYERS          |
| FLASH                |
+----------------------+
複製代碼

檢索節點的深度

咱們已經瞭解了怎麼檢索整個樹,可是若是咱們還須要知道樹中的每一個節點的深度,以便更好的肯定層級結構中的各個節點到底處於什麼位置怎麼辦呢? 咱們能夠經過增長COUNT函數和一條GROUP BY 語句到咱們已存在的查詢語句中來實現顯示整個樹:

SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+----------------------+-------+
| name                 | depth |
+----------------------+-------+
| ELECTRONICS          |     0 |
| TELEVISIONS          |     1 |
| TUBE                 |     2 |
| LCD                  |     2 |
| PLASMA               |     2 |
| PORTABLE ELECTRONICS |     1 |
| MP3 PLAYERS          |     2 |
| FLASH                |     3 |
| CD PLAYERS           |     2 |
| 2 WAY RADIOS         |     2 |
+----------------------+-------+
複製代碼

咱們可使用CONCAT和REPEAT字符串函數來根據深度縮進分類名稱:

SELECT CONCAT( REPEAT(' ', COUNT(parent.name) - 1), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
+-----------------------+
複製代碼

固然,你能夠利用深度值直接在客戶端應用程序上展示層級結構。Web開發者能夠遍歷樹,並根據深度值的變化增長 <li></li><ul></ul> 標籤。

子樹深度

當咱們須要子樹的深度信息時,咱們不能在自鏈接中限制節點或父表,由於它會破壞咱們的結果。相反,咱們添加了一個第三自鏈接,以及一個子查詢以肯定這個深度將做爲子樹的新起點:

SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
        nested_category AS parent,
        nested_category AS sub_parent,
        (
                SELECT node.name, (COUNT(parent.name) - 1) AS depth
                FROM nested_category AS node,
                nested_category AS parent
                WHERE node.lft BETWEEN parent.lft AND parent.rgt
                AND node.name = 'PORTABLE ELECTRONICS'
                GROUP BY node.name
                ORDER BY node.lft
        )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
        AND sub_parent.name = sub_tree.name
GROUP BY node.name
ORDER BY node.lft;

+----------------------+-------+
| name                 | depth |
+----------------------+-------+
| PORTABLE ELECTRONICS |     0 |
| MP3 PLAYERS          |     1 |
| FLASH                |     2 |
| CD PLAYERS           |     1 |
| 2 WAY RADIOS         |     1 |
+----------------------+-------+
複製代碼

這個函數能夠被運用到任何節點上,包括根節點。查詢的深度值老是相對應該指定節點。

查找節點的直接後代

假設你正在一個零售網站上展現電子產品分類。當一個用戶點擊一個分類後,你想要顯示那個分類下面的產品並列出它們的子分類,而不是它們下面的完整分類樹,爲了達到目的,咱們須要顯示它的直接子節點。可是不向下更進一步檢索。例如:咱們顯示PORTABLE ELECTRONICS 分類,咱們想顯示P3 PLAYERS, CD PLAYERS, 和2 WAY RADIOS,可是不想顯示FLASH。

咱們能夠在以前的語句上添加一條HABING語句來輕鬆實現:

SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
        nested_category AS parent,
        nested_category AS sub_parent,
        (
                SELECT node.name, (COUNT(parent.name) - 1) AS depth
                FROM nested_category AS node,
                        nested_category AS parent
                WHERE node.lft BETWEEN parent.lft AND parent.rgt
                        AND node.name = 'PORTABLE ELECTRONICS'
                GROUP BY node.name
                ORDER BY node.lft
        )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
        AND sub_parent.name = sub_tree.name
GROUP BY node.name
HAVING depth <= 1
ORDER BY node.lft;

+----------------------+-------+
| name                 | depth |
+----------------------+-------+
| PORTABLE ELECTRONICS |     0 |
| MP3 PLAYERS          |     1 |
| CD PLAYERS           |     1 |
| 2 WAY RADIOS         |     1 |
+----------------------+-------+
複製代碼

若是想顯示父節點,將HAVING depth <= 1改成HAVING depth = 1

嵌套集合中的聚合函數

讓咱們添加一個產品表來演示聚合函數:

CREATE TABLE product
(
        product_id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(40),
        category_id INT NOT NULL
);

INSERT INTO product(name, category_id) VALUES('20" TV',3),('36" TV',3),
('Super-LCD 42"',4),('Ultra-Plasma 62"',5),('Value Plasma 38"',5),
('Power-MP3 5gb',7),('Super-Player 1gb',8),('Porta CD',9),('CD To go!',9),
('Family Talk 360',10);

SELECT * FROM product;

+------------+-------------------+-------------+
| product_id | name              | category_id |
+------------+-------------------+-------------+
|          1 | 20" TV | 3 | | 2 | 36" TV            |           3 |
|          3 | Super-LCD 42" | 4 | | 4 | Ultra-Plasma 62"  |           5 |
|          5 | Value Plasma 38" | 5 | | 6 | Power-MP3 128mb | 7 | | 7 | Super-Shuffle 1gb | 8 | | 8 | Porta CD | 9 | | 9 | CD To go! | 9 | | 10 | Family Talk 360 | 10 | +------------+-------------------+-------------+ 複製代碼

如今讓咱們來寫一套查詢語句來檢索帶有各分類產品數量的分類樹:

SELECT parent.name, COUNT(product.name)
FROM nested_category AS node ,
        nested_category AS parent,
        product
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.category_id = product.category_id
GROUP BY parent.name
ORDER BY node.lft;

+----------------------+---------------------+
| name                 | COUNT(product.name) |
+----------------------+---------------------+
| ELECTRONICS          |                  10 |
| TELEVISIONS          |                   5 |
| TUBE                 |                   2 |
| LCD                  |                   1 |
| PLASMA               |                   2 |
| PORTABLE ELECTRONICS |                   5 |
| MP3 PLAYERS          |                   2 |
| FLASH                |                   1 |
| CD PLAYERS           |                   2 |
| 2 WAY RADIOS         |                   1 |
+----------------------+---------------------+
複製代碼

這是咱們典型的整樹查詢語句,包含一個COUNT函數和GROUP BY 函數,以及產品表的引用和在WHERE語句中對node和product表的關聯。就像你看到的,每一個分類都計數,子分類的產品數量在父分類上反應出來

添加新的節點

既然咱們以及學會了怎麼檢索咱們的樹,接下來咱們來了解下怎麼添加新的節點到咱們的樹中,咱們再來看看下面的圖表:

image.png

若是我想在TELEVISIONS 和PORTABLE ELECTRONICS 中間添加新的節點,那麼這個新的節點將會給予lft 和 rgt 分別爲10和11(咱們是從左向右編號的),它右邊全部的節點的lft和rgt值都將加2,這些可使用MySql儲存過程解決:

LOCK TABLE nested_category WRITE;

SELECT @myRight := rgt FROM nested_category
WHERE name = 'TELEVISIONS';

UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft + 2 WHERE lft > @myRight;

INSERT INTO nested_category(name, lft, rgt) VALUES('GAME CONSOLES', @myRight + 1, @myRight + 2);

UNLOCK TABLES;
複製代碼

We can then check our nesting with our indented tree query:

SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  GAME CONSOLES        |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
+-----------------------+
複製代碼

若是咱們想給一個沒有子節點的節點添加一個子節點,咱們須要稍微改改咱們的語句。讓咱們在 2 WAY RADIOS節點下面添加新的FRS節點:

LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft FROM nested_category

WHERE name = '2 WAY RADIOS';

UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myLeft;
UPDATE nested_category SET lft = lft + 2 WHERE lft > @myLeft;

INSERT INTO nested_category(name, lft, rgt) VALUES('FRS', @myLeft + 1, @myLeft + 2);

UNLOCK TABLES;
複製代碼

在這個例子中咱們將咱們新的父節點的左值右邊的值所有擴大。而後將改節點插入到父節點左值的右邊:

SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  GAME CONSOLES        |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
|    FRS                |
+-----------------------+
複製代碼

刪除節點

最後咱們瞭解下移除節點。刪除節點時所採起的操做過程取決於節點在層級結構中的位置;刪除葉節點比刪除帶有子節點的節點要容易得多,由於咱們必須處理孤立的節點 刪除葉子節點的過程與添加新的節點正好相反,咱們刪除該節點而且刪除父節點中它的寬度:

LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'GAME CONSOLES';

DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight;

UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight;

UNLOCK TABLES;
複製代碼

咱們再次執行咱們上面的縮進樹的查詢語句來確認咱們刪除了節點且沒有破壞層級結構:

SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
|    FRS                |
+-----------------------+
複製代碼

這個方法對刪除節點和它的子節點一樣有效:

LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'MP3 PLAYERS';

DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight;

UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight;

UNLOCK TABLES;
複製代碼

咱們再次查詢以驗證咱們成功的刪除了整個子樹:

SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  PORTABLE ELECTRONICS |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
|    FRS                |
+-----------------------+
複製代碼

另外一個場景是咱們須要刪除父節點,可是須要保留它的子節點。有時你可能會僅僅是將它的名稱改成一個佔位符直到新的名稱來替換它,有時這些子節點須要提高到被刪除的父節點的層級:

LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'PORTABLE ELECTRONICS';

DELETE FROM nested_category WHERE lft = @myLeft;

UPDATE nested_category SET rgt = rgt - 1, lft = lft - 1 WHERE lft BETWEEN @myLeft AND @myRight;
UPDATE nested_category SET rgt = rgt - 2 WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft - 2 WHERE lft > @myRight;

UNLOCK TABLES;
複製代碼

這裏咱們改節點右側的節點的左右值所有減去2,該節點的全部子節點的左右值所有減去1,咱們再次來驗證下這些元素有沒有被提高:

SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+---------------+
| name          |
+---------------+
| ELECTRONICS   |
|  TELEVISIONS  |
|   TUBE        |
|   LCD         |
|   PLASMA      |
|  CD PLAYERS   |
|  2 WAY RADIOS |
|   FRS         |
+---------------+
複製代碼

總結

相對於鄰接表模型,嵌套集合模型在層級數據的查詢中具備極大的優點,而在數據的插入和刪除操做時則根據插入或刪除數據的位置的不一樣,可能須要更新不少節點甚至是整個樹的左右值,從而影響數據庫的性能。對於客戶端須要頻繁修改表的程序咱們應該避免使用嵌套集合模型,而對於客戶端須要頻繁的查詢表的程序咱們應當使用它,像商城的產品分類表,表的數據只是在後臺維護,而大量的用戶會產生大量的查詢。

相關文章
相關標籤/搜索