提及無限分類..大多數的結構都是 id name parent_id 這種模式.整個結構比較簡單清晰.要構建和更新整個分類也比較容易.可是查詢起來就會很是的麻煩.常常會用到遞歸的算法.例如 獲取某個節點的全部父節點之類. php
今天說一說經過多叉樹的方式構建無限分類,結構上可能會複雜一點,構建和更新也比較麻煩.可是查詢很是方便.兩種方法的優劣就不評論了. node
先看一張圖 mysql
這是咱們要構建的無限分類的模型. 電子產品是最大的分類.家用電器 ,數碼產品是其子分類.能夠看到子分類是被父分類包含起來的.每一個分類都有左右 兩個節點編號分別是一、二、3..... 算法
根據上面的圖mysql中創建表和插入數據 sql
CREATE TABLE `product_categories` ( fetch
`id` MEDIUMINT( 8 ) NOT NULL AUTO_INCREMENT PRIMARY KEY , spa
`name` VARCHAR( 20 ) NOT NULL , code
`left_node` MEDIUMINT( 8 ) NOT NULL , 遞歸
`right_node` MEDIUMINT( 8 ) NOT NULL ci
) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO `product_categories` (`id`, `name`, `left_node`, `right_node`) VALUES
(1, '電子產品', 1, 20),
(2, '家用電器', 2, 9),
(3, '電視機', 3, 4),
(4, '電冰箱', 5, 6),
(5, '空調', 7, 8),
(6, '數碼產品', 10, 19),
(7, '電腦', 11, 18),
(8, '臺式電腦', 12, 13),
(9, '筆記本電腦', 14, 15),
(10, '平板電腦', 16, 17);
表結構以下:
下面是PHP的實例代碼:
一、獲取全部節點
<?php $pdo = new PDO( 'mysql:host=localhost;dbname=test', 'root', '' ); $pdo->exec("SET NAMES UTF8"); $stmt = $pdo->prepare("SELECT c.name FROM product_categories as c, product_categories as p WHERE c.left_node BETWEEN p.left_node AND p.right_node AND p.name='電子產品' ORDER BY c.left_node"); $stmt->execute(); $rs=$stmt->fetchAll(PDO::FETCH_ASSOC); foreach($rs as $v){ echo $v['name'].'<br />'; }輸出:
電子產品
家用電器
電視機
電冰箱
空調
數碼產品
電腦
臺式電腦
筆記本電腦
平板電腦
二、 獲取某個父節點以及其全部子節點
<?php $pdo = new PDO( 'mysql:host=localhost;dbname=test', 'root', '' ); $pdo->exec("SET NAMES UTF8"); $stmt = $pdo->prepare("SELECT c.name FROM product_categories as c, product_categories as p WHERE c.left_node BETWEEN p.left_node AND p.right_node AND p.name='數碼產品' ORDER BY c.left_node"); $stmt->execute(); $rs=$stmt->fetchAll(PDO::FETCH_ASSOC); foreach($rs as $v){ echo $v['name'].'<br />'; }
輸出:
數碼產品
電腦
臺式電腦
筆記本電腦
平板電腦
三、獲取全部的葉子節點
<?php $pdo = new PDO( 'mysql:host=localhost;dbname=test', 'root', '' ); $pdo->exec("SET NAMES UTF8"); $stmt = $pdo->prepare("SELECT name FROM product_categories where right_node-left_node=1"); $stmt->execute(); $rs=$stmt->fetchAll(PDO::FETCH_ASSOC); foreach($rs as $v){ echo $v['name'].'<br />'; }
輸出:
電視機
電冰箱
空調
臺式電腦
筆記本電腦
平板電腦
四、獲取某個子節點及其全部父節點
<?php $pdo = new PDO( 'mysql:host=localhost;dbname=test', 'root', '' ); $pdo->exec("SET NAMES UTF8"); $stmt = $pdo->prepare("SELECT p.name FROM product_categories AS c, product_categories AS p WHERE c.left_node BETWEEN p.left_node AND p.right_node AND c.name = '平板電腦' ORDER BY p.left_node"); $stmt->execute(); $rs=$stmt->fetchAll(PDO::FETCH_ASSOC); foreach($rs as $v){ echo $v['name'].'<br />'; }輸出:
電子產品
數碼產品
電腦
平板電腦
五、獲取全部節點極其所處的層級
<?php $pdo = new PDO( 'mysql:host=localhost;dbname=test', 'root', '' ); $pdo->exec("SET NAMES UTF8"); $stmt = $pdo->prepare("SELECT c.name, (COUNT(p.name) - 1) AS level FROM product_categories AS c, product_categories AS p WHERE c.left_node BETWEEN p.left_node AND p.right_node GROUP BY c.name ORDER BY c.left_node"); $stmt->execute(); $rs=$stmt->fetchAll(PDO::FETCH_ASSOC); var_dump($rs); echo '<br />'; foreach($rs as $v){ echo $v['name'].' level:'.$v['level'].'<br />'; }輸出:
電子產品 level:0
家用電器 level:1
電視機 level:2
電冰箱 level:2
空調 level:2
數碼產品 level:2
電腦 level:2
臺式電腦 level:3
筆記本電腦 level:3
平板電腦 level:3
<?php $pdo = new PDO( 'mysql:host=localhost;dbname=test', 'root', '' ); $pdo->exec("SET NAMES UTF8"); $stmt = $pdo->prepare("SELECT c.name, (COUNT(p.name) - 1) AS level FROM product_categories AS c, product_categories AS p WHERE c.left_node BETWEEN p.left_node AND p.right_node and c.name='平板電腦' GROUP BY c.name ORDER BY c.left_node"); $stmt->execute(); $rs=$stmt->fetchAll(PDO::FETCH_ASSOC); var_dump($rs); echo '<br />'; foreach($rs as $v){ echo $v['name'].' level:'.$v['level'].'<br />'; }輸出:
平板電腦 level:3
七、在某個節點後平行的插入一個節點
<?php $pdo = new PDO( 'mysql:host=localhost;dbname=test', 'root', '' ); $pdo->exec("SET NAMES UTF8"); function addNode($left_node,$new_node){ global $pdo; $stmt = $pdo->prepare("SELECT right_node FROM product_categories WHERE name = '$left_node'"); $stmt->execute(); $rs=$stmt->fetch(PDO::FETCH_ASSOC); $right_node=$rs['right_node']; $pdo->exec("UPDATE product_categories SET right_node = right_node + 2 WHERE right_node > $right_node"); $pdo->exec("UPDATE product_categories SET left_node = left_node + 2 WHERE left_node > $right_node"); $pdo->exec("INSERT INTO product_categories(name, left_node, right_node) VALUES('$new_node', $right_node + 1, $right_node + 2)"); } addNode('家用電器','辦公用品');完成以後表結構以下:
八、刪除某個節點及其全部子節點
<?php $pdo = new PDO( 'mysql:host=localhost;dbname=test', 'root', '' ); $pdo->exec("SET NAMES UTF8"); function deleteNode($node_name){ global $pdo; $stmt = $pdo->prepare("SELECT left_node,right_node, right_node - left_node + 1 as width FROM product_categories WHERE name ='$node_name'"); $stmt->execute(); $rs=$stmt->fetch(PDO::FETCH_ASSOC); $left_node=$rs['left_node']; $right_node=$rs['right_node']; $width=$rs['width']; $pdo->exec("DELETE FROM product_categories WHERE left_node BETWEEN $left_node AND $right_node"); $pdo->exec("UPDATE product_categories SET right_node = right_node - $width WHERE right_node > $right_node"); $pdo->exec("UPDATE product_categories SET left_node = left_node - $width WHERE left_node > $right_node"); } deleteNode('數碼產品');完成以後表結構以下:
能夠看到用多叉樹的方式構建無限分類,查詢的時候是很是簡便的.可是在插入新的節點和刪除節點時就比較麻煩了.