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

MySQL 遞歸查詢單語句實現:利用用戶變量將遞歸轉為迭代

     編輯:關於SqlServer

MySQL 8.0 前不支持 WITH RECURSIVE(CTE 遞歸),但利用 會話用戶變量(@var)在單條 SELECT 中保持狀態,可將樹形遞歸轉為有序迭代,一條 SQL 完成全樹路徑與層級計算。本文提供完整建表、數據、查詢、結果演示。

測試表與數據

CREATE TABLE `treenodes` (
    `id` INT,
    `nodename` VARCHAR(60),
    `pid` INT
);

INSERT INTO `treenodes` (`id`, `nodename`, `pid`) VALUES
(1,'A',0),(2,'B',1),(3,'C',1),(4,'D',2),(5,'E',2),(6,'F',3),
(7,'G',6),(8,'H',0),(9,'I',8),(10,'J',8),(11,'K',8),
(12,'L',9),(13,'M',9),(14,'N',12),(15,'O',12),
(16,'P',15),(17,'Q',15),(18,'R',3),(19,'S',2),(20,'T',6),(21,'U',8);

單語句遞歸查詢

SELECT 
    id AS ID,
    pid AS 父ID,
    levels AS 父到子之間級數,
    paths AS 父到子路徑
FROM (
    SELECT 
        id, pid,
        @le := IF(pid = 0, 0,
            IF(LOCATE(CONCAT('|', pid, ':'), @pathlevel) > 0,
                SUBSTRING_INDEX(SUBSTRING_INDEX(@pathlevel, CONCAT('|', pid, ':'), -1), '|', 1) + 1,
                @le + 1)
        ) AS levels,
        @pathlevel := CONCAT(@pathlevel, '|', id, ':', @le, '|') AS pathlevel,
        @pathnodes := IF(pid = 0, ',0',
            CONCAT_WS(',',
                IF(LOCATE(CONCAT('|', pid, ':'), @pathall) > 0,
                    SUBSTRING_INDEX(SUBSTRING_INDEX(@pathall, CONCAT('|', pid, ':'), -1), '|', 1),
                    @pathnodes
                ),
                pid
            )
        ) AS paths,
        @pathall := CONCAT(@pathall, '|', id, ':', @pathnodes, '|') AS pathall
    FROM treenodes,
         (SELECT @le:=0, @pathlevel:='', @pathall:='', @pathnodes:='') vv
    ORDER BY pid, id  -- 關鍵:父節點必須在子節點前處理
) src
ORDER BY id;

核心機制解析

變量作用更新邏輯
@le當前節點層級(根=0)根節點=0;非根節點:若父節點已處理(@pathlevel 包含父標記)則取父層級+1,否則@le+1(順序保證父先處理)
@pathlevel累積「節點ID:層級」映射串CONCAT(@pathlevel, '|', id, ':', @le, '|'),供子節點查找父層級
@pathnodes當前節點從根到自身的路徑(ID列表)根=',0';非根:若父路徑已知則繼承父路徑+自身pid,否則繼承上一行@pathnodes+pid
@pathall累積所有節點的路徑映射串CONCAT(@pathall, '|', id, ':', @pathnodes, '|'),供後續節點查找父路徑

關鍵前提:ORDER BY pid, id

**父節點必須在子節點之前被讀取**,否則父信息未寫入變量,子節點無法正確繼承層級與路徑。若表無序,需先建臨時表按 pid, id 排序再查詢。

輸出結果示例

ID   父ID   父到子之間級數   父到子路徑
1    0      0               ,0
2    1      1               ,0,1
3    1      1               ,0,1
4    2      2               ,0,1,2
5    2      2               ,0,1,2
6    3      2               ,0,1,3
7    6      3               ,0,1,3,6
8    0      0               ,0
9    8      1               ,0,8
...  ...    ...             ...

適用邊界與替代方案

  • 適用:MySQL 5.6/5.7,單表萬級以內,一次性報表/導出。
  • 不適用:高併發 OLTP(變量非線程安全)、超大表(變量內存壓力)、需頻繁調用(維護性差)。
  • MySQL 8.0+ 首選:WITH RECURSIVE cte AS (SELECT ... UNION ALL SELECT ... JOIN cte ...) SELECT * FROM cte; 標準、可優化、易維護。
  • 存儲過程迭代:臨時表 + 循環,適合舊版本高併發場景。

這個技巧展示了 MySQL 用戶變量在單語句中模擬狀態機的威力,也是理解「遞歸轉迭代」思想的經典案例。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved