萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL 無限級分類實戰:遞歸路徑設計與子權限高效查詢

無限級分類是權限系統、商品分類、組織架構等場景的常見需求。傳統遞歸查詢效能差、程式碼複雜。本文介紹基於 pri_path 路徑編碼 + CONCAT 排序的非遞歸方案,並示範如何高效查詢某節點的所有子孫權限。

一、資料表設計

CREATE TABLE `sh_privilege` (
  `id` TINYINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `pri_name` VARCHAR(15) NOT NULL COMMENT '權限名稱',
  `parent_id` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '上級 ID',
  `pri_level` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '層級 0/1/2...',
  `pri_path` VARCHAR(15) NOT NULL DEFAULT '0' COMMENT '路徑編碼 如 0-2-6',
  PRIMARY KEY (`id`),
  KEY `pri_path` (`pri_path`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='權限表';

關鍵欄位:

  • parent_id:直接父節點 ID
  • pri_path:從根節點到當前節點的完整路徑,如 0-2-6 表示 根→2→6
  • pri_level:層級深度,供縮排顯示用

二、樹形結構排序輸出

利用 CONCAT(pri_path, '-', id) 產生可字典序排序的完整路徑,配合 ORDER BY 即可獲得天然的樹形順序,無需遞歸。

SELECT id, pri_name, pri_path, pri_level
FROM sh_privilege
ORDER BY CONCAT(pri_path, '-', id);

原理: 字串比較天然符合樹的前序遍歷順序,0-1 < 0-1-1 < 0-1-2 < 0-2 ...

三、查詢指定節點的所有子孫權限

利用 LIKE 模糊匹配路徑特徵:%-8-% 表示路徑中包含 -8- 片段(即 8 為祖先)。

SELECT id, pri_name, pri_path
FROM sh_privilege
WHERE CONCAT('-', pri_path, '-') LIKE '%-8-%';

優化建議: 對 pri_path 建立索引,並將查詢改寫為 pri_path LIKE '0-8-%' OR pri_path LIKE '0-%-8-%' 以利用索引前綴匹配(視資料分佈而定)。

四、新增節點時的路徑維護

-- 假設新增節點 parent_id = 6
-- 1. 先查父節點路徑
SELECT pri_path FROM sh_privilege WHERE id = 6; -- 假設回傳 '0-2-6'
-- 2. 新節點 pri_path = CONCAT(父路徑, '-', 新ID)
--    需在 INSERT 後 UPDATE,或使用觸發器/應用層邏輯維護

五、優缺點總結

優點缺點
查詢極快,無遞歸、無 CTE 依賴路徑長度有限制 (VARCHAR 長度)
排序、祖先/子孫查詢皆高效移動子樹需更新所有後代 pri_path
易理解、易維護、相容所有 MySQL 版本深度過大時路徑字串較長

此方案適合「讀多寫少、層級固定、樹規模中等」的業務場景(如權限、選單、地區分類)。若需頻繁移動大型子樹,可考慮 Closure Table (關係表) 方案。

copyright © 萬盛學電腦網 all rights reserved