MySQL:获取父子结构的根节点

时间:2010-08-05 20:41:10

标签: sql 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?

5 个答案:

答案 0 :(得分:6)

实际上,您可以使用函数轻松完成此操作。

尝试在您最喜欢的空测试数据库上运行以下.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

答案 1 :(得分: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

答案 2 :(得分:0)

这在MySQL中很难做到,因为它还不支持递归公用表表达式。

我建议改为使用嵌套集模型,或者将根节点存储在行中,并在结构发生变化时更新它。

答案 3 :(得分:0)

简而言之:不。看看Bill Karwin关于分层模型的优秀演讲,以及它的用途,缺点以及如何解决这些问题:http://www.slideshare.net/billkarwin/models-for-hierarchical-data

答案 4 :(得分: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)