【问题标题】:MYSQL output in tree format OR Adding level (Parent-Child)树格式的 MYSQL 输出或添加级别(父子)
【发布时间】:2015-11-13 23:17:50
【问题描述】:

下面是我桌子上的东西。

我的表

++++++++++++++++++++
Parent   +  Child
++++++++++++++++++++
  C1     +    G1
  C1     +    G2
  C1     +    G3
  G3     +    D1
  G3     +    D2
  C1     +    G4
  G4     +    D3
  G4     +    D4
  C2     +    G5
  C2     +    G6
  C2     +    G7
  C2     +    G8
 ++++++++++++++++++++

什么,我想要如下使用MYSQL。

C1
  G1
  G2
  G3
    D1
    D2
  G4
    D3
    D4

C2
  G5
  G6
  G7
  G8

如果这在 MYSQL 中可行,请告诉我。输出类似于 TREE

更新 1

如果我得到如下所示的新表也可以,这样我就可以使用this example

++++++++++++++++++++++++++++++++++++++++
Parent   +  Child   + PLevel  + CLevel
++++++++++++++++++++++++++++++++++++++++
  C1     +    G1    +   1    +   2
  C1     +    G2    +   1    +   2
  C1     +    G3    +   1    +   2
  G3     +    D1    +   2    +   3
  G3     +    D2    +   2    +   3
  C1     +    G4    +   1    +   2
  G4     +    D3    +   2    +   3
  G4     +    D4    +   2    +   3
  C2     +    G5    +   1    +   2
  C2     +    G6    +   1    +   2
  C2     +    G7    +   1    +   2
  C2     +    G8    +   1    +   2
++++++++++++++++++++++++++++++++++++++++

注意:我从 1 开始级别(例如,我从 0 开始级别)。如果我得到这个级别从 0 开始的新表也可以。

【问题讨论】:

标签: mysql sql tree format parent-child


【解决方案1】:

虽然您不能使用单个查询,但可以使用存储过程...唯一的前提条件是,您需要在现有示例表中再添加 2 条记录来表示“C1”和“ C2" 是顶层... 添加一条记录,其中“父”字段为空白,子级别为“C1”,另一个为“C2”。这将“准备”最顶层的父级。对于后续的层次关联,否则你没有顶层层次的起始“基础”。它还需要一个“主键”列(我在此脚本中将其创建为“IDMyTable”,它只是 1-x 连续的,但假设您的表上有一个自动增量列可供使用)。

我已经包含了所有的输出列来显示它是如何构建的,但是这个例程的前提是根据预期的列输出创建一个表,但在构建时还额外保存了下游的分层表示。为了确保它们在层变深时保持正确的方向,我连接了“ID”列——你会看到它在最终结果集中是如何工作的。

然后,在最终结果集中,我根据层次结构数据的深度预先填充空格。

循环将根据在前面结果集中找到的父记录添加任何记录,但前提是尚未添加 ID(防止重复)...

要查看循环顺序是如何不断附加的,您可以运行最后一个查询而不使用 order by,并查看每次迭代如何限定并添加上一个层次结构级别...

-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$

CREATE DEFINER=`root`@`localhost` PROCEDURE `GetHierarchy2`()
BEGIN
    -- prepare a hierarchy level variable 
    set @hierlvl := 00000;

    -- prepare a variable for total rows so we know when no more rows found
    set @lastRowCount := 0;

    -- pre-drop temp table
    drop table if exists MyHierarchy;

    -- now, create it as the first level you want... 
    -- ie: a specific top level of all "no parent" entries
    -- or parameterize the function and ask for a specific "ID".
    -- add extra column as flag for next set of ID's to load into this.
    create table MyHierarchy as
    select 
            t1.IDMyTable,
            t1.Child AS Parent,
            @hierlvl as IDHierLevel,
            cast( t1.IDMyTable as char(100)) FullHierarchy
        from
            MyTable t1
        where
                t1.Parent is null
            OR t1.Parent = '';


    -- how many rows are we starting with at this tier level
    set @lastRowCount := ROW_COUNT();

    -- we need to have a "primary key", otherwise our UPDATE
    -- statement will nag about an unsafe update command
    alter table MyHierarchy add primary key (IDMyTable);


    -- NOW, keep cycling through until we get no more records
    while @lastRowCount > 0 do

        -- NOW, load in all entries found from full-set NOT already processed
        insert into MyHierarchy
            select 
                    t1.IDMyTable,
                    t1.Child as Parent,
                    h1.IDHierLevel +1 as IDHierLevel,
                    concat_ws( ',', h1.FullHierarchy, t1.IDMyTable ) as FullHierarchy
                from
                    MyTable t1
                        join MyHierarchy h1
                            on t1.Parent = h1.Parent
                    left join
                        MyHierarchy h2
                            on t1.IDMyTable = h2.IDMyTable
                where
                    h2.IDMyTable is null;


        set @lastRowCount := row_count();

        -- now, update the hierarchy level
        set @hierLevel := @hierLevel +1;

    end while;


    -- return the final set now
    select 
            *, concat( lpad( ' ', 1 + (IDHierLevel * 3 ), ' ' ), Parent ) as ShowHierarchy
        from MyHierarchy
        order by FullHierarchy;

END

【讨论】:

    【解决方案2】:

    MySQL 和 RDBMS 通常不擅长这种结构。您可能必须使用客户端递归来执行此操作。

    如果递归仅限于三个深度,就像您的示例一样,您可以使用连接来实现,但对于更深的树来说,它的可扩展性不是很好。

    【讨论】:

    • 不一定是递归;只要语言有引用,您就可以使用没有递归的哈希表来做到这一点。对对象的所有 id 进行哈希处理,让子数组引用其中的任何内容,您可以在单个 while 循环中完成所有操作。
    • 有点跑题了,但你不就是把递归向下移动到语言中吗?在某些时候,肯定需要找到每个节点的每个子节点?
    • @Cylindric:显然,是的 :) 你需要递归来遍历一棵树,例如当你打印它时。我只是说你不需要它来构建它。
    • 现代 DBMS 通常在这方面非常擅长使用递归查询。 MySQL 已经落后了很多年
    【解决方案3】:

    首先为计算级别创建递归函数。

    function fn_CalcLevel(int @ID) 
    As Begin
      Declare @ParentID int
      Select @ParentID = ParentID From Table1 where ID = @ID
    
      IF (@ParentID IS NULL) Return 1 Else Return 1+fn_CalcLevel(@ParentID)
    End
    

    然后创建您的查询,如下所示

    Select *, fn_CalcLevel(Table1.ID) as Level
    From Table1
    

    【讨论】:

      【解决方案4】:

      如果你稍微调整一下你的表格,你可以使用类似的东西:

        SELECT Child,CONCAT(LPAD('',Clevel,' '),Child),etc from tablename
      

      重组是您需要将根节点作为父节点为 0 的行。您可以添加自己的父/子/C 级别的排序来获得所需的序列。

      我知道这是几年前的事了,但它可能会为其他人节省一些精力!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-14
        • 2019-08-23
        • 1970-01-01
        相关资源
        最近更新 更多