日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

php和mysql处理树状_分级_无限分类_分层数据的方法_PHP和MySQL处理树状、分级、无限分类、分层数据的方法...

發(fā)布時(shí)間:2023/12/9 数据库 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php和mysql处理树状_分级_无限分类_分层数据的方法_PHP和MySQL处理树状、分级、无限分类、分层数据的方法... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章標(biāo)題中的多個(gè)詞語(yǔ)表達(dá)的其實(shí)是一個(gè)意思,就是遞歸分類數(shù)據(jù),分級(jí)數(shù)據(jù)非常類似數(shù)據(jù)結(jié)構(gòu)中的樹狀結(jié)構(gòu),即每個(gè)節(jié)點(diǎn)有自己的孩子節(jié)點(diǎn),孩子結(jié)點(diǎn)本身也是父親節(jié)點(diǎn)。這是一個(gè)遞歸、分層形式。可以稱之為樹形層級(jí)數(shù)據(jù)。

層級(jí)數(shù)據(jù)結(jié)構(gòu)是編程語(yǔ)言中非常普通的一種數(shù)據(jù)結(jié)構(gòu),它代表一系列的數(shù)據(jù)每一項(xiàng)都有一個(gè)父親節(jié)點(diǎn)(除了根節(jié)點(diǎn))和其他多個(gè)孩子結(jié)點(diǎn)。WEB開發(fā)人員使用層級(jí)數(shù)據(jù)結(jié)構(gòu)用于非常多的場(chǎng)景,包括內(nèi)容管理系統(tǒng)CMS、論壇主題、郵件列表,還有電子商務(wù)網(wǎng)站的產(chǎn)品分類等。

本文章主要介紹了使用PHP和MYSQL來(lái)管理分級(jí)數(shù)據(jù)的方法,在其中將給出兩種最流行的分級(jí)數(shù)據(jù)模型:

鄰接表模型

嵌套集合模型

鄰接表模型用于分層數(shù)據(jù)

鄰接表模型是一種分級(jí)數(shù)據(jù)模型,其中每個(gè)節(jié)點(diǎn)有一個(gè)指向其父親的指針(根節(jié)點(diǎn)該指針為空值),使用下面的SQL語(yǔ)句將建立該結(jié)構(gòu)并插入測(cè)試數(shù)據(jù):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

--

表的結(jié)構(gòu)

`

category

`

--

CREATE

TABLE

IF

NOT

EXISTS

`

category

`

(

`

category_id

`

int

(

10

)

NOT

NULL

AUTO_INCREMENT

,

`

category_name

`

varchar

(

50

)

NOT

NULL

,

`

parent_id

`

int

(

10

)

DEFAULT

NULL

,

PRIMARY

KEY

(

`

category_id

`

)

)

ENGINE

=

InnoDB

DEFAULT

CHARSET

=

utf8

AUTO_INCREMENT

=

15

;

--

--

轉(zhuǎn)存表中的數(shù)據(jù)

`

category

`

--

INSERT

INTO

`

category

`

(

`

category_id

`

,

`

category_name

`

,

`

parent_id

`

)

VALUES

(

1

,

'A'

,

NULL

)

,

(

2

,

'B'

,

1

)

,

(

3

,

'C'

,

1

)

,

(

4

,

'D'

,

1

)

,

(

5

,

'E'

,

2

)

,

(

6

,

'F'

,

2

)

,

(

7

,

'I'

,

4

)

,

(

8

,

'G'

,

5

)

,

(

9

,

'H'

,

5

)

,

(

10

,

'J'

,

7

)

,

(

11

,

'K'

,

10

)

,

(

12

,

'L'

,

10

)

;

建立完成后,數(shù)據(jù)庫(kù)中存在了數(shù)據(jù),并且分類圖是每個(gè)節(jié)點(diǎn)為(關(guān)鍵字:數(shù)據(jù)庫(kù)ID)。

parent_id就是它的父節(jié)點(diǎn)的ID號(hào),這種方法非常簡(jiǎn)單,因?yàn)槟芎苋菀椎目辞宄缸雨P(guān)系。使用以下的簡(jiǎn)單PHP函數(shù)代碼可以很容易的輸出樹狀路徑:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

function

get_path

(

$category_id

)

{

$con

=

mysql_connect

(

"localhost"

,

"root"

,

"123456"

)

;

if

(

!

$con

)

{

die

(

'數(shù)據(jù)庫(kù)連接失敗: '

.

mysql_error

(

)

)

;

}

mysql_select_db

(

'test'

,

$con

)

;

// 查找當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn)的ID,這里使用表自身與自身連接實(shí)現(xiàn)

$sql

=

"

SELECT c1.parent_id, c2.category_name AS parent_name

FROM category AS c1

LEFT JOIN category AS c2

ON c1.parent_id=c2.category_id

WHERE c1.category_id='$category_id' "

;

//echo $sql."
";//測(cè)試把SQL打印出來(lái),拿到數(shù)據(jù)庫(kù)執(zhí)行一下看看結(jié)果

$result

=

mysql_query

(

$sql

)

;

$row

=

mysql_fetch_array

(

$result

)

;

//現(xiàn)在$row數(shù)組存了父親節(jié)點(diǎn)的ID和名稱信息

// 將樹狀路徑保存在數(shù)組里面

$path

=

array

(

)

;

//如果父親節(jié)點(diǎn)不為空(根節(jié)點(diǎn)),就把父節(jié)點(diǎn)加到路徑里面

if

(

$row

[

'parent_id'

]

!=

NULL

)

{

//將父節(jié)點(diǎn)信息存入一個(gè)數(shù)組元素

$parent

[

0

]

[

'category_id'

]

=

$row

[

'parent_id'

]

;

$parent

[

0

]

[

'category_name'

]

=

$row

[

'parent_name'

]

;

//遞歸的將父節(jié)點(diǎn)加到路徑中

$path

=

array_merge

(

get_path

(

$row

[

'parent_id'

]

)

,

$parent

)

;

}

return

$path

;

}

//根據(jù)上面的圖可以看出,K的ID是11,我們就用它來(lái)測(cè)試路徑

$path

=

get_path

(

11

)

;

echo

"

路徑數(shù)組:

"

;

echo

"

"

;

print_r

(

$path

)

;

echo

"

"

;

//將路徑到根節(jié)點(diǎn)的路徑打印出來(lái)

//打印結(jié)果:J>I>D>A>

echo

"

向根節(jié)點(diǎn)打印路徑:

"

;

for

(

$i

=

count

(

$path

)

-

1

;

$i

>=

0

;

$i

--

)

{

echo

$path

[

$i

]

[

'category_name'

]

.

'>'

;

}

?>

由此可以知道怎樣找到一個(gè)葉子節(jié)點(diǎn)(沒(méi)有孩子的節(jié)點(diǎn))到根節(jié)點(diǎn)的路徑,下面來(lái)看怎樣從根節(jié)點(diǎn)往下來(lái)遍歷層級(jí)結(jié)構(gòu),通過(guò)節(jié)點(diǎn)的層級(jí)關(guān)系來(lái)打印所有的節(jié)點(diǎn):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

function

display_children

(

$category_id

,

$level

)

{

$con

=

mysql_connect

(

"localhost"

,

"root"

,

"123456"

)

;

if

(

!

$con

)

{

die

(

'數(shù)據(jù)庫(kù)連接失敗: '

.

mysql_error

(

)

)

;

}

mysql_select_db

(

'test'

,

$con

)

;

// 獲得當(dāng)前節(jié)點(diǎn)的所有孩子節(jié)點(diǎn)(直接孩子,沒(méi)有孫子)

$result

=

mysql_query

(

"SELECT * FROM category WHERE parent_id='$category_id'"

)

;

// 遍歷孩子節(jié)點(diǎn),打印節(jié)點(diǎn)

while

(

$row

=

mysql_fetch_array

(

$result

)

)

{

// 根據(jù)層級(jí),按照縮進(jìn)格式打印節(jié)點(diǎn)的名字

// 這里只是打印,你可以將以下代碼改成其他,比如把節(jié)點(diǎn)信息存儲(chǔ)起來(lái)

echo

str_repeat

(

'--'

,

$level

)

.

$row

[

'category_name'

]

.

"
"

;

// 遞歸的打印所有的孩子節(jié)點(diǎn)

display_children

(

$row

[

'category_id'

]

,

$level

+

1

)

;

}

}

//根節(jié)點(diǎn)是A:1我們就用它來(lái)打印所有的節(jié)點(diǎn)

display_children

(

1

,

0

)

;

?>

然而,鄰接表模型(每個(gè)節(jié)點(diǎn)存儲(chǔ)父親節(jié)點(diǎn)信息)有它的劣勢(shì),首先使用數(shù)據(jù)庫(kù)的查詢語(yǔ)句很難直接實(shí)現(xiàn)它,需要借助PHP代碼實(shí)現(xiàn)。SQL語(yǔ)句需要你知道節(jié)點(diǎn)位于哪一個(gè)層級(jí)。并且每個(gè)樹層是使用SQL的自我表連接實(shí)現(xiàn)的,這意味著樹的每一層處理都會(huì)降低數(shù)據(jù)庫(kù)的性能。

刪除節(jié)點(diǎn)的過(guò)程也會(huì)導(dǎo)致一些問(wèn)題,如果只刪除了某個(gè)節(jié)點(diǎn)它卻有孩子,結(jié)果是它的孩子成了孤兒(就是沒(méi)有父親了),真正的體現(xiàn)就是,這些孩子節(jié)點(diǎn)從樹中相當(dāng)于“消失了”。

嵌套集合模型用于樹形分層結(jié)構(gòu)數(shù)據(jù)

嵌套集合模型,也叫做先根遍歷樹算法,也是一種處理樹形層級(jí)數(shù)據(jù)的方法。代替節(jié)點(diǎn)間的父子關(guān)系,層級(jí)使用嵌套的容器的集合來(lái)表示,其中每個(gè)節(jié)點(diǎn)具有兩個(gè)值,一個(gè)left,一個(gè)right。

決定left和right的值的過(guò)程是從左到右進(jìn)行的,首先給left賦值,讓后向下遍歷節(jié)點(diǎn)的孩子們,最后才能得到節(jié)點(diǎn)的right的值。SQL語(yǔ)句如下所示:

1

2

3

4

5

6

CREATE

TABLE

category

(

category_id

INT

(

10

)

AUTO_INCREMENT

PRIMARY

KEY

,

category_name

VARCHAR

(

50

)

NOT

NULL

,

lft

INT

(

10

)

NOT

NULL

,

rgt

INT

(

10

)

NOT

NULL

)

;

現(xiàn)在可以用一句SQL查詢得到整個(gè)樹的節(jié)點(diǎn):

SELECT * FROM category WHERE lft BETWEEN 1 AND 14 ORDER BY lft ASC

在本SQL中的兩個(gè)數(shù)字值1和14就是根節(jié)點(diǎn)的left和right值。類似的如果想得到某個(gè)節(jié)點(diǎn)的所有孩子節(jié)點(diǎn),只需要將該SQL語(yǔ)句的1和14替換成本節(jié)點(diǎn)的left和right值就可以了。例如,如果想得到所有男人的衣服,可以用下面的SQL語(yǔ)句:

SELECT * FROM category WHERE lft BETWEEN 2 AND 7 ORDER BY lft ASC

想找到一條到某個(gè)節(jié)點(diǎn)的路徑,用一條SQL語(yǔ)句就可以搞定:

SELECT * FROM category WHERE lft < 9 AND rgt > 10 ORDER BY lft ASC

請(qǐng)仔細(xì)觀察一下一些葉子節(jié)點(diǎn)到根節(jié)點(diǎn)的路徑。就會(huì)發(fā)現(xiàn)所有的祖先都有更小的左值和更大的右值。本例子中一條到裙子分類的路徑被取出。觀察一下裙子的所有l(wèi)eft值都小于9,right值都大于10,其他的非祖先節(jié)點(diǎn)都不滿足該要求。

盡管嵌套集合模型更加復(fù)雜并且有些難以理解,它有非常多的優(yōu)勢(shì)。它不需要依賴其他資源(比如PHP代碼),也不需要遞歸。同時(shí),數(shù)據(jù)庫(kù)查詢語(yǔ)句非常的簡(jiǎn)單,大多數(shù)用一條SQL語(yǔ)句就可以搞定。這些特性都能夠顯著的增加應(yīng)用程序的性能,使得它能夠用可接受的速度來(lái)處理復(fù)雜的層級(jí)結(jié)構(gòu)。

然而萬(wàn)事皆無(wú)完美,更新該層級(jí)結(jié)構(gòu)(增加或刪除節(jié)點(diǎn))卻更加的復(fù)雜,并且可能會(huì)非常慢。

增加一個(gè)節(jié)點(diǎn)到層級(jí)結(jié)構(gòu)的方法:

將一個(gè)節(jié)點(diǎn)插入到層級(jí)數(shù)據(jù)中,需要整個(gè)樹很多節(jié)點(diǎn)的left和right值的更新。例如,如果你想將一個(gè)男士運(yùn)動(dòng)鞋的分類插入到男性衣服的短褲后面。那么所有你必須將大于6的所有l(wèi)eft和right值都增加2。為什么呢?因?yàn)槎萄澋膔ight值是6,那你就必須將你的新分類的left和right值設(shè)定為7和8,當(dāng)然,以下兩條SQL就可以解決:

UPDATE category SET rgt=rgt+2 WHERE rgt>6

UPDATE category SET lft=lft+2 WHERE lft>6

現(xiàn)在樹中間已經(jīng)有空隙用來(lái)插入新分類了,用一條SQL插入該節(jié)點(diǎn):

INSERT INTO category (category_name,lft,rgt) VALUES (

'Sneakers'

,

'7'

,

'8'

)

樹形層級(jí)數(shù)據(jù)中刪除一個(gè)節(jié)點(diǎn)的方法:

在層級(jí)集合模型中刪除一個(gè)節(jié)點(diǎn)的方法,比在鄰接表中相同的操作稍微難一些。不同的操作的復(fù)雜程度是不同的,比如刪除一個(gè)葉子節(jié)點(diǎn)和一個(gè)帶孩子節(jié)點(diǎn)就很不同。

要?jiǎng)h除一個(gè)葉子節(jié)點(diǎn),先將所有l(wèi)eft和right大于該節(jié)點(diǎn)left和right值的節(jié)點(diǎn)的left和right減去2,然后再刪除該節(jié)點(diǎn)。以下使用SQL實(shí)現(xiàn)該過(guò)程:

UPDATE category SET lft=lft-2 WHERE lft>5

UPDATE category SET rgt=rgt-2 WHERE rgt>6

DELETE FROM category WHERE lft=

'5'

AND rgt=

'6'

該例子中短褲節(jié)點(diǎn)被刪除了。

如果要?jiǎng)h除的節(jié)點(diǎn)有孩子節(jié)點(diǎn)的話,刪除過(guò)程會(huì)多一個(gè)步驟:

比如我們刪除男性衣服分類的時(shí)候:

UPDATE category SET lft=lft-1, rgt=rgt-1 WHERE lft>2 AND rgt<7

UPDATE category SET lft=lft-2 WHERE lft>7

UPDATE category SET rgt=rgt-2 WHERE rgt>7

DELETE FROM category WHERE lft=

'2'

AND rgt=

'7'

哪種模型對(duì)于處理樹形分層數(shù)據(jù)更好?

哪種情況更好呢,看情況。如果需要一個(gè)更加靈活的模型,更容易更新,就用鄰接表模型吧。如果分類構(gòu)成了一棵復(fù)雜的數(shù),并且更新不需要很頻繁,用嵌套集合模型肯定是上上之選。

本文內(nèi)容翻譯自:訪問(wèn),其中原文中的代碼有些問(wèn)題,本人添加了測(cè)試數(shù)據(jù)并改正了代碼。

總結(jié)

以上是生活随笔為你收集整理的php和mysql处理树状_分级_无限分类_分层数据的方法_PHP和MySQL处理树状、分级、无限分类、分层数据的方法...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。