'mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式'

MySQL 數據結構 設計 程序不就是0和1 2019-07-19
"

有時候我們的應用會保存一些樹形的數據結構,比如論壇回帖、公司的組織架構、商品分類、知識庫等相關的目錄樹結構,這些數據存在一種遞歸關係,那麼今天討論下我們怎麼去存儲樹形結構?

"

有時候我們的應用會保存一些樹形的數據結構,比如論壇回帖、公司的組織架構、商品分類、知識庫等相關的目錄樹結構,這些數據存在一種遞歸關係,那麼今天討論下我們怎麼去存儲樹形結構?

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

傳統做法

一般開發人員都會設計類似這樣的一個表結構,每條記錄都存儲著上一條記錄的父節點,可能是這樣一個表結構:

CREATE TABLE `t_comment` (
`cmmonent_id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`comments` text,
PRIMARY KEY (`cmmonent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

實際數據可能是這樣:

"

有時候我們的應用會保存一些樹形的數據結構,比如論壇回帖、公司的組織架構、商品分類、知識庫等相關的目錄樹結構,這些數據存在一種遞歸關係,那麼今天討論下我們怎麼去存儲樹形結構?

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

傳統做法

一般開發人員都會設計類似這樣的一個表結構,每條記錄都存儲著上一條記錄的父節點,可能是這樣一個表結構:

CREATE TABLE `t_comment` (
`cmmonent_id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`comments` text,
PRIMARY KEY (`cmmonent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

實際數據可能是這樣:

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

如果這樣的表結構,當一個主題的討論的內容很多的時候,就需要編寫比較複雜的代碼進行遞歸檢索很多記錄,查詢的效率就會很低。相反,如果數據量不大的話,討論內容相對固定,數據層數較少,那麼這種方案還是比較簡單的,這種情況下此結構還是符合需求的。但是不能保證一個帖子不能成為熱帖,那就可能要半夜重啟服務器了 。

我們這裡討論兩種更通用,擴張性更好的解決方案:路徑枚舉和閉包表。

路徑枚舉

對於上面的表(t_comment)結構,我們可以增加一個字段path,用於記錄節點的所有父信息。記錄的方式是把所有的父信息組織稱一個字符串。類似下表的形式:

"

有時候我們的應用會保存一些樹形的數據結構,比如論壇回帖、公司的組織架構、商品分類、知識庫等相關的目錄樹結構,這些數據存在一種遞歸關係,那麼今天討論下我們怎麼去存儲樹形結構?

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

傳統做法

一般開發人員都會設計類似這樣的一個表結構,每條記錄都存儲著上一條記錄的父節點,可能是這樣一個表結構:

CREATE TABLE `t_comment` (
`cmmonent_id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`comments` text,
PRIMARY KEY (`cmmonent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

實際數據可能是這樣:

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

如果這樣的表結構,當一個主題的討論的內容很多的時候,就需要編寫比較複雜的代碼進行遞歸檢索很多記錄,查詢的效率就會很低。相反,如果數據量不大的話,討論內容相對固定,數據層數較少,那麼這種方案還是比較簡單的,這種情況下此結構還是符合需求的。但是不能保證一個帖子不能成為熱帖,那就可能要半夜重啟服務器了 。

我們這裡討論兩種更通用,擴張性更好的解決方案:路徑枚舉和閉包表。

路徑枚舉

對於上面的表(t_comment)結構,我們可以增加一個字段path,用於記錄節點的所有父信息。記錄的方式是把所有的父信息組織稱一個字符串。類似下表的形式:

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

因為path路徑存儲了該節點所有的父親節點信息,所以我們可以很容易的獲取到某個節點的所有的父親節點,例如我們可以用程序先獲取到path的取值字符串,然後再用字符串拆分函數處理所有的父節點。

如果要查詢某個節點的所有後代,例如查找comment_id等於3的所有後代,可以用如下查詢語句:

select * from comments where path like '1/2/3_%';
"

有時候我們的應用會保存一些樹形的數據結構,比如論壇回帖、公司的組織架構、商品分類、知識庫等相關的目錄樹結構,這些數據存在一種遞歸關係,那麼今天討論下我們怎麼去存儲樹形結構?

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

傳統做法

一般開發人員都會設計類似這樣的一個表結構,每條記錄都存儲著上一條記錄的父節點,可能是這樣一個表結構:

CREATE TABLE `t_comment` (
`cmmonent_id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`comments` text,
PRIMARY KEY (`cmmonent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

實際數據可能是這樣:

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

如果這樣的表結構,當一個主題的討論的內容很多的時候,就需要編寫比較複雜的代碼進行遞歸檢索很多記錄,查詢的效率就會很低。相反,如果數據量不大的話,討論內容相對固定,數據層數較少,那麼這種方案還是比較簡單的,這種情況下此結構還是符合需求的。但是不能保證一個帖子不能成為熱帖,那就可能要半夜重啟服務器了 。

我們這裡討論兩種更通用,擴張性更好的解決方案:路徑枚舉和閉包表。

路徑枚舉

對於上面的表(t_comment)結構,我們可以增加一個字段path,用於記錄節點的所有父信息。記錄的方式是把所有的父信息組織稱一個字符串。類似下表的形式:

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

因為path路徑存儲了該節點所有的父親節點信息,所以我們可以很容易的獲取到某個節點的所有的父親節點,例如我們可以用程序先獲取到path的取值字符串,然後再用字符串拆分函數處理所有的父節點。

如果要查詢某個節點的所有後代,例如查找comment_id等於3的所有後代,可以用如下查詢語句:

select * from comments where path like '1/2/3_%';
mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

如果要查詢下一層的子節點,可以使用如下查詢語句:

SELECT * from t_comment where path regexp "1/2/3/[0-9]$";
"

有時候我們的應用會保存一些樹形的數據結構,比如論壇回帖、公司的組織架構、商品分類、知識庫等相關的目錄樹結構,這些數據存在一種遞歸關係,那麼今天討論下我們怎麼去存儲樹形結構?

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

傳統做法

一般開發人員都會設計類似這樣的一個表結構,每條記錄都存儲著上一條記錄的父節點,可能是這樣一個表結構:

CREATE TABLE `t_comment` (
`cmmonent_id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`comments` text,
PRIMARY KEY (`cmmonent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

實際數據可能是這樣:

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

如果這樣的表結構,當一個主題的討論的內容很多的時候,就需要編寫比較複雜的代碼進行遞歸檢索很多記錄,查詢的效率就會很低。相反,如果數據量不大的話,討論內容相對固定,數據層數較少,那麼這種方案還是比較簡單的,這種情況下此結構還是符合需求的。但是不能保證一個帖子不能成為熱帖,那就可能要半夜重啟服務器了 。

我們這裡討論兩種更通用,擴張性更好的解決方案:路徑枚舉和閉包表。

路徑枚舉

對於上面的表(t_comment)結構,我們可以增加一個字段path,用於記錄節點的所有父信息。記錄的方式是把所有的父信息組織稱一個字符串。類似下表的形式:

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

因為path路徑存儲了該節點所有的父親節點信息,所以我們可以很容易的獲取到某個節點的所有的父親節點,例如我們可以用程序先獲取到path的取值字符串,然後再用字符串拆分函數處理所有的父節點。

如果要查詢某個節點的所有後代,例如查找comment_id等於3的所有後代,可以用如下查詢語句:

select * from comments where path like '1/2/3_%';
mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

如果要查詢下一層的子節點,可以使用如下查詢語句:

SELECT * from t_comment where path regexp "1/2/3/[0-9]$";
mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

插入操作的話,先複製一下父節點的路徑,再加上新comment_id的值。路徑枚舉的方式使得查詢子節點和父節點都變得更加簡單,通過查看分隔符個數就即可知道節點的層次,雖然增加了一個字段存儲數據,應用程序需要額外增加代碼校驗路徑信息的正確性,但是這種設計擴張性更好,更能適應未來數據的不斷增加。我們這個設計中仍然保留了parent_id列,是為了一些操作更加方便,也可以用來校驗路徑信息的正確性。

閉包表

閉包表也是一種通用的方案,它需要額外增加一張表,用於記錄節點的關係。它這個節點關係不僅包括父子同時記錄所有節點之間的關係。可以簡單理解就是多對多關係中的中間表。只不過它是閉包結構,就是自己跟自己進行關聯。

例如表結構:

create table t_path(
parent_id int(11) not null,
sub_id int(11) not null,
primary key(parent_id,sun_id)
)

實際數據可能是這樣:

"

有時候我們的應用會保存一些樹形的數據結構,比如論壇回帖、公司的組織架構、商品分類、知識庫等相關的目錄樹結構,這些數據存在一種遞歸關係,那麼今天討論下我們怎麼去存儲樹形結構?

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

傳統做法

一般開發人員都會設計類似這樣的一個表結構,每條記錄都存儲著上一條記錄的父節點,可能是這樣一個表結構:

CREATE TABLE `t_comment` (
`cmmonent_id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`comments` text,
PRIMARY KEY (`cmmonent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

實際數據可能是這樣:

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

如果這樣的表結構,當一個主題的討論的內容很多的時候,就需要編寫比較複雜的代碼進行遞歸檢索很多記錄,查詢的效率就會很低。相反,如果數據量不大的話,討論內容相對固定,數據層數較少,那麼這種方案還是比較簡單的,這種情況下此結構還是符合需求的。但是不能保證一個帖子不能成為熱帖,那就可能要半夜重啟服務器了 。

我們這裡討論兩種更通用,擴張性更好的解決方案:路徑枚舉和閉包表。

路徑枚舉

對於上面的表(t_comment)結構,我們可以增加一個字段path,用於記錄節點的所有父信息。記錄的方式是把所有的父信息組織稱一個字符串。類似下表的形式:

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

因為path路徑存儲了該節點所有的父親節點信息,所以我們可以很容易的獲取到某個節點的所有的父親節點,例如我們可以用程序先獲取到path的取值字符串,然後再用字符串拆分函數處理所有的父節點。

如果要查詢某個節點的所有後代,例如查找comment_id等於3的所有後代,可以用如下查詢語句:

select * from comments where path like '1/2/3_%';
mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

如果要查詢下一層的子節點,可以使用如下查詢語句:

SELECT * from t_comment where path regexp "1/2/3/[0-9]$";
mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

插入操作的話,先複製一下父節點的路徑,再加上新comment_id的值。路徑枚舉的方式使得查詢子節點和父節點都變得更加簡單,通過查看分隔符個數就即可知道節點的層次,雖然增加了一個字段存儲數據,應用程序需要額外增加代碼校驗路徑信息的正確性,但是這種設計擴張性更好,更能適應未來數據的不斷增加。我們這個設計中仍然保留了parent_id列,是為了一些操作更加方便,也可以用來校驗路徑信息的正確性。

閉包表

閉包表也是一種通用的方案,它需要額外增加一張表,用於記錄節點的關係。它這個節點關係不僅包括父子同時記錄所有節點之間的關係。可以簡單理解就是多對多關係中的中間表。只不過它是閉包結構,就是自己跟自己進行關聯。

例如表結構:

create table t_path(
parent_id int(11) not null,
sub_id int(11) not null,
primary key(parent_id,sun_id)
)

實際數據可能是這樣:

mysql 開發技巧(一):這才是正確存儲樹形結構數據的方式

有了這個關係表,查找子節點和父節點就很簡單了,相當於單表操作,例如統計節點3的所有的子節點,可以查詢這個關係表 t_path;

select count(1) from t_path where parent_id =3 and sub_id <>3;

總結

無論是新增字段還是新增一個表用來存儲節點關係,都是一種典型的空間換取時間的方案,相比較來講還是閉包表的關係更容易維護,就是單純的單表增刪改查操作。count某個父節點的個數就是深度,如果不想額外count,可以增加一個字段path_length來表示深度,那完全就是簡單的查詢操作了。

"

相關推薦

推薦中...