2010-08-05 44 views
3

的根節點我有一個類似的表:MySQL的:獲取母子結構

================= 
| Id | ParentId | 
================= 
| 1 | 0  | 
-----+----------- 
| 2 | 1  | 
-----+----------- 
| 3 | 0  | 
-----+----------- 
| 4 | 3  | 
-----+----------- 
| 5 | 3  | 
-----+----------- 
| 6 | 0  | 
-----+----------- 
| 7 | 6  | 
-----+----------- 
| 8 | 7  | 
----------------- 

獲得一個ID,我需要知道它的根「節點」 ID。所以,

  • 鑑於1,返回1
  • 鑑於2,返回1
  • 鑑於3,返回3
  • 鑑於4,返回3
  • 鑑於5,返回3
  • 鑑於6 ,退貨6
  • 鑑於7,退貨6
  • 鑑於8,退貨7

層次結構的層次沒有限制。有沒有可以做我需要的SQL?

回答

0

這在MySQL中很難做到,因爲它還不支持遞歸公用表表達式。

我建議改爲使用嵌套集模型,否則將行中的根節點存儲並在結構更改時更新它。

5

實際上,您可以使用函數輕鬆完成此操作。

嘗試在您最喜歡的空測試數據庫上運行以下.sql腳本。

-- 
-- Create the `Nodes` table 
-- 
CREATE TABLE `Nodes` (
    `Id` INT NOT NULL PRIMARY KEY 
    ,`ParentId` INT NOT NULL 
) ENGINE=InnoDB; 

-- 
-- Put your test data into it. 
-- 
INSERT INTO `Nodes` (`Id`, `ParentId`) 
VALUES 
    (1, 0) 
, (2, 1) 
, (3, 0) 
, (4, 3) 
, (5, 3) 
, (6, 0) 
, (7, 6) 
, (8, 7); 

-- 
-- Enable use of ; 
-- 
DELIMITER $$ 

-- 
-- Create the function 
-- 
CREATE FUNCTION `fnRootNode` 
(
    pNodeId INT 
) 
RETURNS INT 
BEGIN 
    DECLARE _Id, _ParentId INT; 

    SELECT pNodeId INTO _ParentId; 

    my_loop: LOOP 
     SELECT 
      `Id` 
      ,`ParentId` 
     INTO 
      _Id 
      ,_ParentId 
     FROM `Nodes` 
     WHERE `Id` = _ParentId; 

     IF _ParentId = 0 THEN 
      LEAVE my_loop; 
     END IF; 
    END LOOP my_loop; 

    RETURN _Id; 
END; 
$$ 

-- 
-- Re-enable direct querying 
-- 
DELIMITER ; 


-- 
-- Query the table using the function to see data. 
-- 
SELECT 
    fnRootNode(`Nodes`.`Id`) `Root` 
    ,`Nodes`.`Id` 
    ,`Nodes`.`ParentId` 
FROM `Nodes` 
ORDER BY 
    fnRootNode(`Nodes`.`Id`) ASC 
; 

-- EOF 

輸出將是:

Root Id ParentId 
==== ==== ======== 
1 1 0 
1 2 1 
3 3 0 
3 4 3 
3 5 3 
6 6 0 
6 7 6 
6 8 7 
+0

到目前爲止,我已經找到了最好的解決方案。所有其他人都表示,由於MySQL沒有遞歸函數,這是不可能的。 +1 – Benjamin 2013-10-03 13:21:41

1

下面是一個簡短的查詢做你問,假設你的表稱爲foo和你想知道的<id>根:

SELECT f.Id 
FROM (
    SELECT @id AS _id, (SELECT @id := ParentId FROM foo WHERE Id = _id) 
    FROM (SELECT @id := <id>) tmp1 
    JOIN foo ON @id <> 0 
    ) tmp2 
JOIN foo f ON tmp2._id = f.Id 
WHERE f.ParentId = 0 
0

我用@Kris回答了一會兒,直到我遇到了一個問題,其中一個子節點可能被刪除(意外),因此函數進入一個無限循環第二掛起MySQL數據庫可言,下面是修改後的版本,這在我的情況下工作:

DELIMITER $$ 

CREATE FUNCTION `FindRootNode`(InputValue INT(11)) RETURNS INT(11) 
    NO SQL 
BEGIN 

DECLARE ReturnValue, _ParentId INT; 

SELECT InputValue INTO _ParentId; 

REPEAT 
    SET ReturnValue = _ParentId; 
    SELECT IFNULL((SELECT parent_id FROM TableName WHERE id=ReturnValue), 0) INTO _ParentId; 

    UNTIL _ParentId = 0 
END REPEAT; 

RETURN ReturnValue; 

END $$ 

DELIMITER ; 

Usage1

SELECT CompanyCategoryTestRoot(HERE_COMES_CHILD_NODE_VALUE)