【问题标题】:MySQL Hierarchical Structure Data ExtractionMySQL分层结构数据提取
【发布时间】:2010-11-12 22:13:54
【问题描述】:

我现在在一个查询上已经挣扎了大约 2 个小时。帮助? :(

我有一张这样的桌子:

id 名称 lft rgt 35 顶层板 1 16 37 二级板 3 6 15 38 二级板 2 4 5 39 二级板 1 2 3 40 三层板 1 13 14 41 三层板 2 9 12 42 三级板 3 7 8 43 四层板 1 10 11

它存储在this tutorial 推荐的结构中。我想要做的是选择一个论坛板和所有子论坛低于所选论坛板的一级(不低于)。理想情况下,查询将获得所选论坛的级别,同时仅传递董事会的 ID,然后它将选择该论坛,以及它的所有直系子级。

所以,我希望最终得到:

id 名称 lft rgt 35 顶层板 1 16 37 二级板 3 6 15 38 二级板 2 4 5 39 二级板 1 2 3

或者

id 名称 lft rgt 37 二级板 3 6 15 40 三层板 1 13 14 41 三层板 2 9 12 42 三级板 3 7 8

这里的顶行是父论坛,其他子论坛。另外,我想要一个给出深度值的东西,其中深度是相对于所选父表单的。例如,将最后一张表作为一些工作数据,我们会:

id 名称 lft rgt 深度 37 二级板 3 6 15 0 40 三层板 1 13 14 1 41 三层板 2 9 12 1 42 三层板 3 7 8 1

或者

id 名称 lft rgt 深度 35 顶层板 1 16 0 37 二级板 3 6 15 1 38 二级板 2 4 5 1 39 二级板 1 2 3 1

我希望你能明白我的意思。

有人可以帮忙吗?现在真的让我很生气:(

詹姆斯

【问题讨论】:

  • MySQL 没有分层功能。如果您使用具有分层查询支持的替代数据库,例如 Oracle 或 SQL Server (2005+) Express,则使用起来会更容易。
  • 我知道这是一篇 postgres 文章,但值得一读 explainextended.com/2009/09/24/…

标签: mysql hierarchical-data


【解决方案1】:

对您来说最简单的方法 - 只需在您保持深度的位置添加一列。 否则查询将非常低效-您将必须获得整个层次结构,按左数排序(将第一个孩子放在第一位),将其连接到自身以确保对于每个下一个节点左数等于前一个节点右数+1

一般来说,嵌套区间算法很好,但有一个严重的缺点——如果你向树中添加一些东西,需要进行大量的重新计算。 一个不错的选择是带有连分数的 Tropashko 嵌套区间算法 - 只需 google 即可。使用这种算法在父级以下获得一个级别是非常自然的。此外,给定一个孩子,您可以计算其所有父母的所有数字,而无需访问数据库。

【讨论】:

    【解决方案2】:

    要考虑的另一件事是,关系数据库确实不是存储分层数据的最佳和自然方式。像这里这样的结构 - 本质上是二叉树 - 将更容易用 XML blob 来表示,您可以持久化或将其作为对象存储在面向对象的数据库中。

    【讨论】:

    • -1 我还没有看到 XML 的人产生任何实质性的东西。
    【解决方案3】:

    我自己更喜欢邻接表方法。以下示例使用非递归存储过程返回树/子树,然后我将其转换为 XML DOM,但您可以对结果集执行任何您喜欢的操作。请记住,这是从 PHP 到 MySQL 的一次调用,并且邻接列表更易于管理。

    完整的脚本在这里:http://pastie.org/1294143

    PHP

    <?php
    
    header("Content-type: text/xml");
    
    $conn = new mysqli("localhost", "foo_dbo", "pass", "foo_db", 3306);
    
    // one non-recursive db call to get the tree
    
    $result = $conn->query(sprintf("call department_hier(%d,%d)", 2,1));
    
    $xml = new DomDocument;
    $xpath = new DOMXpath($xml);
    
    $dept = $xml->createElement("department");
    $xml->appendChild($dept);
    
    // loop and build the DOM
    
    while($row = $result->fetch_assoc()){
    
        $staff = $xml->createElement("staff");
        // foreach($row as $col => $val) $staff->setAttribute($col, $val); 
    
        $staff->setAttribute("staff_id", $row["staff_id"]); 
        $staff->setAttribute("name", $row["name"]); 
        $staff->setAttribute("parent_staff_id", $row["parent_staff_id"]); 
    
        if(is_null($row["parent_staff_id"])){
            $dept->setAttribute("dept_id", $row["dept_id"]); 
            $dept->setAttribute("department_name", $row["department_name"]); 
            $dept->appendChild($staff);
        }
        else{
            $qry = sprintf("//*[@staff_id = '%d']", $row["parent_staff_id"]);
            $parent = $xpath->query($qry)->item(0);
            if(!is_null($parent)) $parent->appendChild($staff);
        }
    }
    $result->close();
    $conn->close();
    
    echo $xml->saveXML();
    ?>
    

    XML 输出

    <department dept_id="2" department_name="Mathematics">
        <staff staff_id="1" name="f00" parent_staff_id="">
            <staff staff_id="5" name="gamma" parent_staff_id="1"/>
            <staff staff_id="6" name="delta" parent_staff_id="1">
                <staff staff_id="7" name="zeta" parent_staff_id="6">
                    <staff staff_id="2" name="bar" parent_staff_id="7"/>
                    <staff staff_id="8" name="theta" parent_staff_id="7"/>
                </staff>
            </staff>
        </staff>
    </department>
    

    SQL 东西

    -- TABLES
    
    drop table if exists staff;
    create table staff
    (
    staff_id smallint unsigned not null auto_increment primary key,
    name varchar(255) not null
    )
    engine = innodb;
    
    drop table if exists departments;
    create table departments
    (
    dept_id tinyint unsigned not null auto_increment primary key,
    name varchar(255) unique not null
    )
    engine = innodb;
    
    drop table if exists department_staff;
    create table department_staff
    (
    dept_id tinyint unsigned not null,
    staff_id smallint unsigned not null,
    parent_staff_id smallint unsigned null,
    primary key (dept_id, staff_id),
    key (staff_id),
    key (parent_staff_id)
    )
    engine = innodb;
    
    -- STORED PROCEDURES
    
    drop procedure if exists department_hier;
    
    delimiter #
    
    create procedure department_hier
    (
    in p_dept_id tinyint unsigned,
    in p_staff_id smallint unsigned
    )
    begin
    
    declare v_done tinyint unsigned default 0;
    declare v_dpth smallint unsigned default 0;
    
    create temporary table hier(
     dept_id tinyint unsigned,
     parent_staff_id smallint unsigned, 
     staff_id smallint unsigned, 
     depth smallint unsigned
    )engine = memory;
    
    insert into hier select dept_id, parent_staff_id, staff_id, v_dpth from department_staff 
        where dept_id = p_dept_id and staff_id = p_staff_id;
    
    /* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
    
    create temporary table tmp engine=memory select * from hier;
    
    while not v_done do
    
        if exists( select 1 from department_staff e 
                inner join hier on e.dept_id = hier.dept_id and e.parent_staff_id = hier.staff_id and hier.depth = v_dpth) then
    
            insert into hier select e.dept_id, e.parent_staff_id, e.staff_id, v_dpth + 1 from department_staff e 
                inner join tmp on e.dept_id = tmp.dept_id and e.parent_staff_id = tmp.staff_id and tmp.depth = v_dpth;
    
            set v_dpth = v_dpth + 1;            
    
            truncate table tmp;
            insert into tmp select * from hier where depth = v_dpth;
    
        else
            set v_done = 1;
        end if;
    
    end while;
    
    select 
     hier.dept_id,
     d.name as department_name,
     s.staff_id,
     s.name,
     p.staff_id as parent_staff_id,
     p.name as parent_name,
     hier.depth
    from 
     hier
    inner join departments d on hier.dept_id = d.dept_id
    inner join staff s on hier.staff_id = s.staff_id
    left outer join staff p on hier.parent_staff_id = p.staff_id;
    
    drop temporary table if exists hier;
    drop temporary table if exists tmp;
    
    end #
    
    delimiter ;
    
    -- TEST DATA
    
    insert into staff (name) values 
        ('f00'),('bar'),('alpha'),('beta'),('gamma'),('delta'),('zeta'),('theta');
    
    insert into departments (name) values
     ('Computing'),('Mathematics'),('English'),('Engineering'),('Law'),('Music');
    
    insert into department_staff (dept_id, staff_id, parent_staff_id) values
    (1,1,null), 
        (1,2,1), 
        (1,3,1), 
            (1,4,3),
                (1,7,4),
    (2,1,null), 
        (2,5,1), 
        (2,6,1), 
            (2,7,6),
                (2,8,7),
                (2,2,7);
    
    -- TESTING (call this sproc from your php)
    
    call department_hier(1,1);
    
    call department_hier(2,1);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-13
      • 2018-08-08
      • 1970-01-01
      • 1970-01-01
      • 2015-03-12
      • 2023-03-31
      相关资源
      最近更新 更多