Mysql 遞歸 查詢所有父節點


准備數據

CREATE TABLE `demo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `parent_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

INSERT INTO `demo` VALUES ('1', 'A', '0');
INSERT INTO `demo` VALUES ('2', 'B', '1');
INSERT INTO `demo` VALUES ('3', 'C', '1');
INSERT INTO `demo` VALUES ('4', 'D', '2');
INSERT INTO `demo` VALUES ('5', 'E', '4');
INSERT INTO `demo` VALUES ('6', 'F', '1');
INSERT INTO `demo` VALUES ('7', 'G', '1');
id    name  parent_id 

1        A        0
2        B        1
3        C        1
4        D        2
5        E        4
6        F        1
7        G        1

需求:根據一個子ID,查詢所有父類

 

SELECT t2.id, t2.`name`
FROM
    (
        SELECT 
        @r AS _id,
        (SELECT @r := parent_id FROM demo WHERE id = _id) AS parent_id,
        @l := @l + 1 AS lvl
        FROM
        (SELECT @r := 5, @l := 0) vars, demo AS h
        WHERE @r <> 0 
    ) t1
JOIN demo t2
ON t1._id = t2.Id

結果如下

 

id      name
1         A
2         B
4         D
5         E

分析過程(答疑過程)

 我使用將SQL語句拆分的方式,並展示每句SQL運行結果,讓各位可以看的更明白,以便於根據你自己的業務需求進行更改

 1、先不管T2,先把T1的SQL抽出來看

SELECT 
        @r AS _id,
        (SELECT @r := parent_id FROM demo WHERE id = _id) AS parent_id,
        @l := @l + 1 AS lvl
        FROM
        (SELECT @r := 5, @l := 0) vars, demo AS h
        WHERE @r <> 0 

 

從這里可以看出,其實T1已經找出ID為5節點的所有父節點了,和T2(即demo表)進行左連接,只不過是為了根據Id獲取Name而已。而且還可以看出@l其實在整個SQL中並沒有什么作用,只是用來標識節點的等級,底級子節點的lvl為1,父節點lvl值越大表示越靠近頂級父節點,想象一下樹結構,你就明白了

 2、搞明白@r := 5 

SELECT @r := 5, @l := 0

從這里看出,其實@r,@l 就是一個變量而已,
作為變量自然你可以隨便起名,當然也可以隨便賦值,改成@a,@b也都是可以的
那這句SQL的意思就出來了,它表示給變量@r賦值,值為5,給@l賦值,值為0
因此,其實整個SQL的意思也明了了就是根據子ID5,查詢所有父類

3、搞懂變量@r 值如何變化

@r AS _id,
        (SELECT @r := parent_id FROM demo WHERE id = _id) AS parent_id

根據MYSQL執行順序(在文章末尾處),SQL語句在執行時會先執行From,即會先執行上一步的賦值操作,因此這里的_id值為5,所以上面的SQL等同於如下SQL

4、所以上面的SQL等同於如下SQL

SELECT @r := parent_id FROM demo WHERE id = 5

 

 

 

這句SQL肯定沒人不會,但是有一個點很重要,它是整個執行過程的核心,就是@r := parent_id,它在查詢Id為5的節點的父Id時,把這個父Id同時賦給了變量@r。因此@r值改變了,它從5變為了4。

這個時候思路就很清晰了,只要我們設置限制條件,讓SQL在@r為0的時候結束循環就OK了

5、<>符號的含義

WHERE @r <> 0

這個就沒什么好說的了,<>符號在MYSQL中表示不等於,這就是我們在第四步中所說的限制條件,它限制了@r這個變量不能等於0。所以當@r不等於0時,SQL語句會根據子ID向上查詢父ID,又把父ID當做子ID賦值給@r,再次向上查詢,直至@r變量的值為0為止。

補充:如果業務不需要,可以完全可以去掉@l變量

 

SELECT@a AS _id, 
        (SELECT @a := parent_id FROM demo WHERE id = _id) AS parent_id
    FROM 
        (SELECT @a := 5) vars, 
        demo h 
    WHERE @a <> 0

可以看出@l其實在整個SQL中並沒有什么作用,只是用來標識節點的等級,這也證實了我們上述第一步的關於@l的解釋

因為這個過程很類似於JAVA通過構造樹結構向上遞歸查詢的方式,所以這個SQL形式被很多人叫做MYSQL遞歸查詢

關於MYSQL的查詢順序

1.FROM
2.ON
3.JOIN
4.WHERE
5.GROUP By
6.CUBE|ROllUP
7.HAVING
8.SELECT
9.DISTINCT
10.ORDER BY
11.LIMIT
最先執行的是FROM操作,最后執行的是LIMIT操作。每個操作都會產生一個虛擬表,該虛擬表作為一個處理的輸入

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM