代码之家  ›  专栏  ›  技术社区  ›  Matthias Günter

分层获取所有父级,无需使用CTE查询

  •  2
  • Matthias Günter  · 技术社区  · 8 年前

    介绍

    有很多答案很好地解释了如何从父子关系中读取分层数据。我使用的是mySQL,我创建了一个查询,可以读取所有的父对象( parent_id )(通过逗号连接)对于给定的 id

    create table `menu` (
        `id` double ,
        `title` varchar (765),
        `controller` varchar (765),
        `method` varchar (765),
        `url` varchar (765),
        `parent_id` varchar (765),
        `added_date` datetime ,
        `updated_date` datetime 
    );
    

    http://sqlfiddle.com/#!9/48d276f/171 . 应运行查询 无热膨胀系数

    SELECT GROUP_CONCAT(T2.id) AS parents
    FROM (
        SELECT
            @r AS _id,
            (SELECT @r := parent_id FROM menu WHERE id = _id) AS parent_id,
            @l := @l + 1 AS lvl
        FROM (SELECT @r := 31, @l := 0) vars, menu m
        WHERE @r <> 0) T1
    JOIN menu T2
    ON T1._id = T2.id
    ORDER BY T1.lvl DESC;
    

    查询结果为:

    parents
    -------------
    3,17,31
    

    挑战:

    倍数 身份证件 我以为子查询会有帮助,但我得到了一个错误( Unknown table 'T3' in field list

    预期结果应为:

    id      | parents
    -----------------------
    25      | 5,25
    31      | 3,17,31
    23      | 4,23
    

    使用的查询( http://sqlfiddle.com/#!9/48d276f/180 ):

    SELECT T3.id, T4.parents
    FROM menu T3, (SELECT T3.id, GROUP_CONCAT(T2.id) AS parents
      FROM (
          SELECT
              @r AS _id,
              (SELECT @r := parent_id FROM menu WHERE id = _id) AS parent_id,
              @l := @l + 1 AS lvl
          FROM (SELECT @r := T3.id, @l := 0) vars, menu m
          WHERE @r <> 0) T1
      JOIN menu T2
      ON T1._id = T2.id
      ORDER BY T1.lvl DESC) T4
    WHERE T3.id IN (25, 31, 23)
    
    1 回复  |  直到 8 年前
        1
  •  2
  •   Solarflare    8 年前

    通过使用另一个连接,您可以应用与单个id相同的逻辑,因此基本上会多次重复该计算。由于不能再使用固定的开始值,我对条件进行编码,以重新初始化交叉连接中的变量(“reset\u r”)。

    请尝试以下操作:

    SELECT t1.id, GROUP_CONCAT(t1.r ORDER BY t1.lvl DESC) AS parents
    FROM (
      SELECT
        t0.r_init AS id, 
        @r := IF(t0.reset_r = 1, t0.r_init, 
                  (select parent_id from menu where id = @r)) AS r,
        @l := IF(t0.reset_r = 1, 1, @l + 1) AS lvl
      FROM 
        (SELECT m0.id as counter, m1.id AS r_init,
           ((SELECT min(id) FROM menu) = m0.id) AS reset_r 
         FROM menu m0, menu m1
         WHERE m1.id IN (25, 31, 23)
        ) t0 
      ORDER BY t0.r_init, t0.counter
    ) t1
    WHERE t1.r <> 0
    -- or instead of "where":
    -- JOIN menu t2 ON t2.id = t1.r;
    GROUP BY t1.id;
    

    对于大型表,您应该限制 p different data model . 此外,尽管您的表结构可能是一个示例,但您显然应该使用主键(否则父项和重置条件定义不明确)和相同的数据类型 parent_id id

    使现代化 :MySQL 5.6查询的一个版本(应该可以在 sql-fiddle

    SELECT t2.id, GROUP_CONCAT(t2.r ORDER BY t2.lvl DESC)
    FROM (
      SELECT id, r, lvl
      FROM (
        SELECT
          t0.r_init AS id,
          @r := IF(t0.reset_r = 1, t0.r_init, 
                    (select parent_id from menu where id = @r)) AS r,
          @l := IF(t0.reset_r = 1, 1, @l + 1) AS lvl
        FROM 
          (SELECT m0.id as counter, m1.id AS r_init,
             ((SELECT min(id) FROM menu) = m0.id) AS reset_r 
           FROM menu m0, menu m1
           WHERE m1.id IN (25, 31, 23)
           ORDER BY r_init, counter
          ) t0 
        ORDER BY t0.r_init, t0.counter
      ) t1
      WHERE r <> 0
    ) t2
    GROUP BY t2.id;