准備數據
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操作。每個操作都會產生一個虛擬表,該虛擬表作為一個處理的輸入