首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 無限分類算法比較:遞歸、Path 路徑與左右值模型

     編輯:PHP 相關知識

三大主流方案對比

維度遞歸查詢 (Adjacency List)Path 路徑 (Materialized Path)左右值 (Nested Set)
表結構id, parent_id, nameid, parent_id, name, pathid, name, lft, rgt
查詢子樹多次查詢/遞歸 PHP單查詢 WHERE path LIKE '%,1,%'單查詢 WHERE lft>2 AND rgt<11
查詢祖先遞歸向上單查詢 WHERE FIND_IN_SET(id, REPLACE(path,',',','))單查詢 WHERE lft<2 AND rgt>11
插入節點簡單簡單 (path = parent.path + id + ',')複雜 (需更新所有後續 lft/rgt +2)
移動子樹簡單 (改 parent_id)中等 (批量更新 path 前綴)極其複雜 (需重算大量區間)
刪除子樹遞歸刪除單查詢 DELETE WHERE path LIKE '%,3,%'單查詢但需修復間隙
適合場景層級淺、變動頻繁讀多寫少、層級深、需完整路徑極少變動、頻繁讀取子樹/祖先

Path 路徑模型實現細節 (推薦通用)

// 表結構
CREATE TABLE categories (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    parent_id INT UNSIGNED DEFAULT 0,
    name VARCHAR(100) NOT NULL,
    path VARCHAR(500) NOT NULL COMMENT '如: ,1,5,12,',
    depth TINYINT UNSIGNED DEFAULT 0,
    sort INT DEFAULT 0,
    INDEX idx_path (path),
    INDEX idx_parent (parent_id)
) ENGINE=InnoDB;

// 新增節點
function addNode($parentId, $name) {
    $pdo = getPDO();
    if ($parentId == 0) {
        $path = ',';
        $depth = 0;
    } else {
        $stmt = $pdo->prepare("SELECT path, depth FROM categories WHERE id = ?");
        $stmt->execute([$parentId]);
        $parent = $stmt->fetch();
        $path = $parent['path'] . $parentId . ',';
        $depth = $parent['depth'] + 1;
    }
    $stmt = $pdo->prepare("INSERT INTO categories (parent_id, name, path, depth) VALUES (?, ?, ?, ?)");
    $stmt->execute([$parentId, $name, $path, $depth]);
    $newId = $pdo->lastInsertId();
    // 更新自身 path 包含自己
    $pdo->prepare("UPDATE categories SET path = CONCAT(path, ?, ',') WHERE id = ?")->execute([$newId, $newId]);
    return $newId;
}

// 獲取完整樹 (按 path 排序即為層級順序)
function getTree() {
    return getPDO()->query("SELECT id, name, path, depth FROM categories ORDER BY path")->fetchAll();
}

// 顯示縮進
foreach (getTree() as $node) {
    echo str_repeat('  ', $node['depth']) . htmlspecialchars($node['name']) . "
"; } // 移動子樹 (將節點 12 移至新父節點 8) function moveSubtree($nodeId, $newParentId) { $pdo = getPDO(); $pdo->beginTransaction(); try { // 1. 獲取舊路徑前綴與新路徑前綴 $old = $pdo->prepare("SELECT path FROM categories WHERE id = ?")->execute([$nodeId])->fetchColumn(); $newParent = $pdo->prepare("SELECT path, id FROM categories WHERE id = ?")->execute([$newParentId])->fetch(); $newPrefix = $newParent['path'] . $newParent['id'] . ','; $depthDiff = substr_count($newPrefix, ',') - substr_count($old, ','); // 深度差 // 2. 批量更新 path 與 depth $pdo->prepare("UPDATE categories SET path = REPLACE(path, ?, ?), depth = depth + ? WHERE path LIKE ?") ->execute([$old, $newPrefix, $depthDiff, $old . '%']); $pdo->commit(); } catch (Exception $e) { $pdo->rollBack(); throw $e; } }

左右值模型適用場景

  • 分類幾乎不變 (如地理區劃、圖書館分類法)。
  • 需要高性能獲取「所有後代」或「所有祖先」。
  • 不適合頻繁增刪改的業務分類。

選型建議

  1. 優先 Path 路徑: 實現簡單、查詢靈活、移動子樹只需批量 UPDATE path 前綴,適合 90% 業務場景。
  2. 極簡場景用遞歸: 層級 ≤ 3、數據量 < 1000、變動極頻繁,PHP 遞歸 + 緩存即可。
  3. 只讀/極少寫用左右值: 如多級評論展示、固定分類導航。
  4. 資料庫原生支援: PostgreSQL ltree、MySQL 8.0 WITH RECURSIVE 可直接遞歸查詢,無需冗余字段。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved