Mysql递归查询获取父类

时间:2017-03-23 15:49:51

标签: mysql left-join recursive-query

我有3张桌子:

  1. 用户
  2. user_groups
  3. 用户可以在多个(子)组中。它们存储在user_groups表中,就像这样

    +--------------+--------------+---------------+
    | id           | user_id      | group_id      | 
    +--------------+--------------+---------------+
    | 1            | 1            | 23            |
    +--------------+--------------+---------------+
    | 2            | 2            | 24            |
    -----------------------------------------------
    

    现在在我的groups表格中,排名靠前的是parent_id = 0

    +--------------+--------------+---------------+
    | id           | parent_id    | name          | 
    +--------------+--------------+---------------+
    | 1            | 2            | Group 1.1     |
    +--------------+--------------+---------------+
    | 2            | 0            | Group 1       |
    +--------------+--------------+---------------+
    | 3            | 2            | Group 1.2     | 
    +--------------+--------------+---------------+
    | 4            | 3            | Group 1.2.1   |
    +--------------+--------------+---------------+
    | 5            | 2            | Group 1.3     |
    +--------------+--------------+---------------+
    

    现在我想构建一个查询,它为我提供了所有用户的所有父组。我做了一些关于递归查询的研究,我找到了这个特别的帖子: How to create a MySQL hierarchical recursive query

    但是当我加入桌子时,我不知道如何处理这个问题。

    这是我到目前为止所得到的:

    SELECT
        `users`.`id`,
        `users`.`first_name`,
        `users`.`last_name`,
        `users`.`email`,
        `users`.`language`,
        `groups`.`name`,
        `groups`.`parent_id`
    FROM `users`
        LEFT JOIN `user_groups`
            ON `user_groups`.`user_id` = `users`.`id`
        LEFT JOIN `groups`
            ON `groups`.`id` = `user_groups`.`group_id`
    WHERE
        `users`.`created` 
        BETWEEN
            DATE_SUB(NOW(), INTERVAL 365 DAY) AND NOW()
    

    但是这个查询只能得到子组的名称和id。我想要的是顶级组。

    感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

典型的解决方案是创建一个存储函数,通过跟踪父项,直到找到parent_id = 0的行,返回任何给定组的顶级组。

然后,您可以将该功能应用于用户所属的每个组,并选择一组不同的顶级组。

这样的事情对你有用:

delimiter $$

drop function if exists get_top_level_group_id $$

create function get_top_level_group_id (p_group_id int) returns int
begin 
  declare v_return_val int;
  declare v_group_id int;
  declare v_parent_id int;
  declare continue handler for not found
    begin
      return -1;
    end;

  set v_group_id = p_group_id;
  set v_parent_id = p_group_id;

  while v_parent_id != 0 
  do 
    set v_group_id = v_parent_id;

    select `parent_id` 
    into v_parent_id 
    from `groups` 
    where id = v_group_id;
  end while;
  return v_group_id;
end $$

delimiter ;

然后,您可以像这样更新您的查询,以获取这些用户及其不同的顶级群组:

SELECT DISTINCT
    `users`.`id`,
    `users`.`first_name`,
    `users`.`last_name`,
    `users`.`email`,
    `users`.`language`,
    get_top_level_group_id(`user_groups`.`group_id`) as top_level_group_id
FROM `users`
    LEFT JOIN `user_groups`
        ON `user_groups`.`user_id` = `users`.`id`
WHERE
    `users`.`created` 
    BETWEEN
        DATE_SUB(NOW(), INTERVAL 365 DAY) AND NOW()