我有一张类似的表:
=================
| Id | ParentId |
=================
| 1 | 0 |
-----+-----------
| 2 | 1 |
-----+-----------
| 3 | 0 |
-----+-----------
| 4 | 3 |
-----+-----------
| 5 | 3 |
-----+-----------
| 6 | 0 |
-----+-----------
| 7 | 6 |
-----+-----------
| 8 | 7 |
-----------------
鉴于Id,我需要知道它的根“节点”ID。所以,
层次结构的级别没有限制。是否有可以满足我需要的SQL?
答案 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)