mysql - 如何查询 MySql 表以显示根及其子项。

标签 mysql sql parent-child hierarchy

UserID      UserName       ParentID      TopID
  1         abc            Null           Null
  2         edf             1             1
  3         gef             1             1
  4         huj             3             1
  5         jdi             4             1
  6         das             2             1
  7         new            Null           Null
  8         gka             7             7

TopID和ParentID来自于userID

我想获取用户记录及其子项和子项记录。这里 userid1 是 root,它的 child 是 userid2 和 userid 3。所以如果用户 id 是 1,我必须显示从 userid 1 到 userid 6 的所有记录,因为它们都是 root 的 child 和 SUbchild。同样,对于 userid3,我必须显示 userid3 及其子 Userid 4 和 Userid 4 的子 Userid5 如果用户id是3

输出应该是

Userid  Username
3          gef
4          huj
5          jdi

我会知道 userid 和 topID,那么我该如何进行查询才能获得上述结果。

SELECT UserID, UserName  FROM tbl_User WHERE ParentID=3 OR UserID=3 And TopID=1;

通过上面的查询,我能够显示 userid 3 和 userid 4 我无法显示 userid 5,有点被它击中了。需要帮忙。谢谢

最佳答案

使用存储过程在 MySQL 中进行递归分层查询在技术上是可行的。

这是一个适合您的场景的:

CREATE TABLE `user` (
  `UserID` int(16) unsigned NOT NULL,
  `UserName` varchar(32),
  `ParentID` int(16) DEFAULT NULL,
  `TopID` int(16) DEFAULT NULL,
  PRIMARY KEY (`UserID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO user VALUES (1, 'abc', NULL, NULL), (2, 'edf', 1, 1), (3, 'gef', 1, 1), 
 (4, 'huj', 3, 1), (5, 'jdi', 4, 1), (6, 'das', 2, 1), (7, 'new', NULL, NULL), 
 (8, 'gka', 7, 7);

DELIMITER $$
DROP PROCEDURE IF EXISTS `Hierarchy` $$
CREATE PROCEDURE `Hierarchy` (IN GivenID INT, IN initial INT)
BEGIN
    DECLARE done INT DEFAULT 0;
    DECLARE next_id INT;

    -- CURSOR TO LOOP THROUGH RESULTS --
    DECLARE cur1 CURSOR FOR SELECT UserID FROM user WHERE ParentID = GivenID;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    -- CREATE A TEMPORARY TABLE TO HOLD RESULTS --
    IF initial=1 THEN
        -- MAKE SURE TABLE DOESN'T CONTAIN OUTDATED INFO IF IT EXISTS (USUALLY ON ERROR) --
        DROP TABLE IF EXISTS OUT_TEMP; 
        CREATE TEMPORARY TABLE OUT_TEMP (userID int, UserName varchar(32));
    END IF;

    -- ADD OURSELF TO THE TEMPORARY TABLE --
    INSERT INTO OUT_TEMP SELECT UserID, UserName FROM user WHERE UserID = GivenID;

    -- AND LOOP THROUGH THE CURSOR --
    OPEN cur1;
    read_loop: LOOP
        FETCH cur1 INTO next_id;

        -- NO ROWS FOUND, LEAVE LOOP --
        IF done THEN
        LEAVE read_loop;
        END IF;

        -- NEXT ROUND --
        CALL Hierarchy(next_id, 0);     
    END LOOP;

    CLOSE cur1;

    -- THIS IS THE INITIAL CALL, LET'S GET THE RESULTS --
    IF initial=1 THEN
    SELECT * FROM OUT_TEMP;
        -- CLEAN UP AFTER OURSELVES --
        DROP TABLE OUT_TEMP; 
    END IF;
END $$
DELIMITER ;

CALL Hierarchy(3,1);
+--------+----------+
| userID | UserName |
+--------+----------+
|      3 | gef      |
|      4 | huj      |
|      5 | jdi      |
+--------+----------+
3 rows in set (0.07 sec)

Query OK, 0 rows affected (0.07 sec)

CALL Hierarchy(1,1);
+--------+----------+
| userID | UserName |
+--------+----------+
|      1 | abc      |
|      2 | edf      |
|      6 | das      |
|      3 | gef      |
|      4 | huj      |
|      5 | jdi      |
+--------+----------+
6 rows in set (0.10 sec)

Query OK, 0 rows affected (0.10 sec)

是时候指出一些注意事项了:

  • 由于这是递归调用存储过程,您需要增加 max_sp_recursion_depth 的大小,最大值为 255(默认为 0)。

  • 我在具有有限测试数据(user 表的 10 个元组)的非繁忙服务器上的结果需要 0.07-0.10 秒才能完成。性能如此,最好将递归放在您的应用程序层中。

  • 我没有利用您的 TopID 列,因此可能存在逻辑缺陷。但是这两个测试用例给了我预期的结果。

免责声明:这个例子只是为了表明它可以在 MySQL 中完成,而不是我无论如何都认可它。存储过程、临时表和游标可能不是解决此问题的最佳方法。

关于mysql - 如何查询 MySql 表以显示根及其子项。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7999770/

相关文章:

sql - 从表中选择 5 行,但专门指定了一行

c# - .NET/SQLite。插入父/子记录;我如何获得外键?

java - 如何从 JInternalFrame 父级中处理子级?

php - 从连接了 2 个属性的表中选择产品

mysql - 使用基于另一个已填充表的 INNER JOIN UPDATE 语句更新空表

php - 有没有办法在浏览器中查看当前查询响应时间?

mysql - 如何在我的 sql 列上添加序列号值

mysql - 表中有实际但重复的数据或引用其 id 并运行两个查询或更复杂的查询?

php - 如何获取两个表的结果以及如何在标题中显示一个字段值

c - 父子线程程序的奇怪行为