mysql - 分层获取所有父级而不使用 CTE 查询

标签 mysql sql hierarchy

简介

有很多答案很好地解释了如何从父子关系中读取分层数据。我正在使用 mySQL,并且创建了一个查询,该查询读取给定 id 的所有父级 (parent_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 。该查询应不带 CTE 运行,目前如下所示:

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

挑战:

我想创建相同的查询,它允许我读取多个 id 的父级。我认为子查询会有所帮助,但在将 id 传递给子查询时出现错误(字段列表中的未知表“T3”)。

预期结果应该是:

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)

最佳答案

您可以通过使用另一个连接来应用与单个 id 相同的逻辑,从而基本上重复该评估多次。由于您不能再使用固定的起始值,因此我对条件进行了编码以重新初始化交叉连接中的变量(“reset_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_idid

更新: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;

关于mysql - 分层获取所有父级而不使用 CTE 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46462785/

相关文章:

php - 在php中将字符串添加到数组中的字符串

MySQL Workbench 使用已知良好的在线开放数据库测试连接

sql - postgresql 触发器中的 CURRENT_DATE

sql - 使用 Oracle sql 根据父值更新子行

sql获取自引用表中的平均工资

mysql - 从列中选择最大值,除非其他列中有特定值

android - 将本地设备 SQLite 同步到我的 mySQL 服务器

sql - Microsoft RAP(风险评估计划)

php - 如何更正php mysql查询

jsf - Spring Security 3.1.4 taglib 授权/身份验证不适用于 Tomcat 7 上的 JSF 2.2 中的角色层次结构