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

发布于 2024-12-13 19:28:44 字数 1001 浏览 0 评论 0原文

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,它的子项是 userid2 和 userid 3。因此,如果用户 id 是 1,我必须显示从 userid 1 到 userid 6 的所有记录,因为所有记录都是 root 的子项和子项。同样,对于 userid3,我必须显示 userid3 及其子 Userid 4 和 Userid 4 Userid5 的子代 如果 userid 是 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;

通过上面的查询,我能够显示用户 ID 3 和用户 ID 4,但我无法显示用户 ID 5,有点震惊。需要帮助。谢谢

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 and ParentID is from the userID

I Want to get a user record and its child and subchild record. Here userid1 is the root and its child are userid2 and userid 3. So If the user id is 1 I have to display all the records from userid 1 to userid 6 since all are child and SUbchild of the root. Similarly for userid3 I have to display userid3 and its child Userid 4 and Child of Userid 4 Userid5
if the userid is 3

output should be

Userid  Username
3          gef
4          huj
5          jdi

I will know the userid and the topID so how can I do the query to acheive the above result.

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

By the above query I am able to display userid 3 and userid 4 I am not able to display userid 5, Kind of struck in it. Need help. Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

誰ツ都不明白 2024-12-20 19:28:44

从技术上讲,可以使用存储过程在 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中完成,无论如何我并不认可它。存储过程、临时表和游标可能不是解决此问题的最佳方法。

It is technically possible to do recursive hierarchical queries in MySQL using stored procedures.

Here is one adapted to your scenario:

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)

Time to point out some caveats:

  • Since this is recursively calling a stored procedure, you need to increase the size of max_sp_recursion_depth, which has a max value of 255 (defaults to 0).

  • My results on a non-busy server with the limited test data (10 tuples of the user table) took 0.07-0.10 seconds to complete. The performance is such that it might be best to put the recursion in your application layer.

  • I didn't take advantage of your TopID column, so there might be a logic flaw. But the two test-cases gave me the expected results.

Disclaimer: This example was just to show that it can be done in MySQL, not that I endorse it in anyway. Stored Procedures, temporary tables and cursors are perhaps not the best way to do this problem.

黑凤梨 2024-12-20 19:28:44

好吧,这不是一个非常干净的实现,但由于您只需要子级和子级,因此其中任何一个都可以工作:

查询 1:

SELECT UserID, UserName
FROM tbl_user
WHERE ParentID = 3 OR UserID = 3
UNION
SELECT UserID, UserName
FROM tbl_user
WHERE ParentID IN (SELECT UserID
FROM tbl_user
WHERE ParentID = 3);

查询 2:

SELECT UserID, UserName
FROM tbl_user 
WHERE UserID = 3
OR ParentID = 3
OR ParentID IN (SELECT UserID
    FROM tbl_user
    WHERE ParentID = 3);

编辑 1:或者,您可以修改表结构以更方便地查询 的所有子级一个特定的类别。请点击此链接阅读有关在 MySQL 中存储分层数据的更多信息。

此外,您可能会考虑以树状方式分层存储数据,这在中得到了很好的解释这篇文章

请注意,每种方法在检索所需结果与添加/删除类别方面都有其权衡,但我相信您会喜欢阅读。

Well not a pretty clean implementation but since you need only the children and sub-children, either of these might work:

Query1:

SELECT UserID, UserName
FROM tbl_user
WHERE ParentID = 3 OR UserID = 3
UNION
SELECT UserID, UserName
FROM tbl_user
WHERE ParentID IN (SELECT UserID
FROM tbl_user
WHERE ParentID = 3);

Query 2:

SELECT UserID, UserName
FROM tbl_user 
WHERE UserID = 3
OR ParentID = 3
OR ParentID IN (SELECT UserID
    FROM tbl_user
    WHERE ParentID = 3);

EDIT 1: Alternatively, you may modify your table structure to make it more convenient to query all children of a particular category. Please follow this link to read more on storing hierarchical data in MySQL.

Also, you may think on storing your data hierarchically in a tree-like fashion that is very well explained in this article.

Please note that each method has its trade-offs with respect to retrieving desired results vs adding/removing categories but I'm sure you'll enjoy the reading.

许久 2024-12-20 19:28:44

这是我见过的最好的文章之一,解释了在 SQL 样式数据库中存储树状数据的“修改的预序树遍历”方法。

http://www.sitepoint.com/hierarchical-data-database/

MPTT内容从第 2 页开始。

本质上,您为树中的每个节点存储“Left”和“Right”值,以这样的方式获取 ParentA 的所有子节点,您将获得 Left和适合那么父A

SELECT * 
FROM TableName
WHERE Left > ParentLeft 
AND Right < ParentRight

This is one of the best articles I've seen for explaining the "Modified Preorder Tree Traversal" method of storing tree-like data in a SQL-style database.

http://www.sitepoint.com/hierarchical-data-database/

The MPTT stuff starts on page 2.

Essentially, you store a "Left" and a "Right" value for each node in the tree, in such a manner that to get all children of ParentA, you get the Left and Right for ParentA, then

SELECT * 
FROM TableName
WHERE Left > ParentLeft 
AND Right < ParentRight
ˉ厌 2024-12-20 19:28:44

要获取所选子项的所有父项(本例中 user_id = 3):

         SELECT
              @parent_id AS _user_id,
              user_name,
              (
                 SELECT @parent_id := parent_id
                 FROM users
                 WHERE user_id = _user_id
              ) AS parent
           FROM (
        -- initialize variables
              SELECT
                 @parent_id := 3
              ) vars,
              users u
           WHERE @parent_id <> 0;

<代码>

获取选定 user_id 的所有子级

SELECT ui.user_id AS 'user_id', ui.user_name AS 'user_name', parent_id,
FROM
(
  SELECT connect_by_parent(user_id) AS user_id
  FROM (
    SELECT
      @start_user := 3,
      @user_id := @start_user
  ) vars, users
  WHERE @user_id IS NOT NULL
  ) uo
JOIN users ui ON ui.user_id = uo.user_id

这需要以下函数

CREATE FUNCTION connect_by_parent(value INT) RETURNS INT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
        DECLARE _user_id INT;
        DECLARE _parent_id INT;
        DECLARE _next INT;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET @user_id = NULL;

SET _parent_id = @user_id;
SET _user_id = -1;

IF @user_id IS NULL THEN
        RETURN NULL;
END IF;

LOOP
        SELECT  MIN(user_id)
        INTO    @user_id
        FROM    users
        WHERE   parent_id = _parent_id
                AND user_id > _user_id;

        IF @user_id IS NOT NULL OR _parent_id = @start_with THEN
                RETURN @user_id;
        END IF;

        SELECT  user_id, parent_id
        INTO    _user_id, _parent_id
        FROM    users
        WHERE   user_id = _parent_id;
END LOOP;

结束

<代码>

此示例大量使用许多 sql 用户可能不熟悉的会话变量,因此这里的链接可能会提供一些见解:会话变量

To get all of the parents of the selected child (user_id = 3 in this example):

         SELECT
              @parent_id AS _user_id,
              user_name,
              (
                 SELECT @parent_id := parent_id
                 FROM users
                 WHERE user_id = _user_id
              ) AS parent
           FROM (
        -- initialize variables
              SELECT
                 @parent_id := 3
              ) vars,
              users u
           WHERE @parent_id <> 0;

To get all of the children of a selected user_id

SELECT ui.user_id AS 'user_id', ui.user_name AS 'user_name', parent_id,
FROM
(
  SELECT connect_by_parent(user_id) AS user_id
  FROM (
    SELECT
      @start_user := 3,
      @user_id := @start_user
  ) vars, users
  WHERE @user_id IS NOT NULL
  ) uo
JOIN users ui ON ui.user_id = uo.user_id

This requires the following function

CREATE FUNCTION connect_by_parent(value INT) RETURNS INT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
        DECLARE _user_id INT;
        DECLARE _parent_id INT;
        DECLARE _next INT;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET @user_id = NULL;

SET _parent_id = @user_id;
SET _user_id = -1;

IF @user_id IS NULL THEN
        RETURN NULL;
END IF;

LOOP
        SELECT  MIN(user_id)
        INTO    @user_id
        FROM    users
        WHERE   parent_id = _parent_id
                AND user_id > _user_id;

        IF @user_id IS NOT NULL OR _parent_id = @start_with THEN
                RETURN @user_id;
        END IF;

        SELECT  user_id, parent_id
        INTO    _user_id, _parent_id
        FROM    users
        WHERE   user_id = _parent_id;
END LOOP;

END

This example heavily uses session variables which many sql users may be unfamiliar with, so here's a link that may provide some insight: session variables

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文