MySQL层次结构数据提取

MySQL层次结构数据提取,mysql,hierarchical-data,Mysql,Hierarchical Data,我已经为一个问题挣扎了大约2个小时。帮助?:( 我有一张这样的桌子: id name lft rgt 35 Top level board 1 16 37 2nd level board 3 6 15 38 2nd level board 2 4 5 39 2nd level boar

我已经为一个问题挣扎了大约2个小时。帮助?:(

我有一张这样的桌子:

id name lft rgt 35 Top level board 1 16 37 2nd level board 3 6 15 38 2nd level board 2 4 5 39 2nd level board 1 2 3 40 3rd level board 1 13 14 41 3rd level board 2 9 12 42 3rd level board 3 7 8 43 4th level board 1 10 11 id名称lft rgt 35顶级董事会1 16 37二级董事会36 15 38第二级董事会2 4 5 39第二级董事会1 2 3 40第三级董事会1 13 14 41第三级董事会2 9 12 42第三级董事会378 43第四级董事会11011 它存储在中建议的结构中。我要做的是选择一个论坛板和所有子论坛,低于所选论坛板的一个级别(不低于该级别)。理想情况下,查询将获得所选论坛的级别,同时只传递板的ID,然后它将选择该论坛及其所有直接子论坛

因此,我希望最终能够:

id name lft rgt 35 Top level board 1 16 37 2nd level board 3 6 15 38 2nd level board 2 4 5 39 2nd level board 1 2 3 id名称lft rgt 35顶级董事会1 16 37二级董事会36 15 38第二级董事会2 4 5 39第二级董事会1 2 3 或

id名称lft rgt 37二级董事会36 15 40第三级董事会1 13 14 41第三级董事会2 9 12 42第三级董事会378 这里的顶行是父论坛,其他的子论坛。另外,我想要一个给定深度值的地方,深度是相对于所选父窗体的。例如,将最后一个表作为一些工作数据,我们将有:

id name lft rgt depth 37 2nd level board 3 6 15 0 40 3rd level board 1 13 14 1 41 3rd level board 2 9 12 1 42 3rd level board 3 7 8 1 id名称lft rgt深度 37二级董事会36 15 0 40第三层板1 13 14 1 41第三级董事会2 9 12 1 42第三级董事会378 1 或

id名称lft rgt深度 35顶级董事会116 0 37二级董事会36 15 1 38第二层板2 4 5 1 39第二级董事会1 2 3 1 我希望你明白我的意思

有人能帮忙吗?现在我真的很烦:(


James

最简单的方法就是添加一列,在其中保留深度。 否则,查询将非常低效-您将必须获得整个层次结构的一个索引,按左数排序(这将使第一个子元素排在第一位),将其连接到自身,以确保下一个节点的左数等于上一个节点的右数+1

一般来说,嵌套区间算法是不错的,但有一个严重的缺点——如果您向树中添加了一些内容,则需要进行大量的重新计算。
一个很好的替代方案是Topasko嵌套区间算法,它是连续的分数,只是它的谷歌。用这种算法得到一个低于父级的单元格是非常自然的。而且,对于一个孩子,你可以计算所有父母的所有数字而不必撞到数据库。抽象数据库并不是存储分层数据的最理想、最自然的方式。像您这里的结构(本质上是二叉树)更容易用XML blob表示,您可以将其持久化,或者作为对象存储在面向对象数据库中。

我自己更喜欢邻接列表方法。下面的考试ple使用非递归存储过程返回树/子树,然后将其转换为XML DOM,但您可以使用resultset执行任何操作。请记住,这是从PHP到MySQL的单个调用,邻接列表更易于管理

全文如下:

PHP
MySQL没有层次结构功能。如果您使用其他数据库,例如Oracle或SQL Server(2005+)Express,它具有层次结构查询支持,那么使用MySQL会更容易。我知道这是一篇postgres文章,但它是一篇很好的读物-1我还没有看到XML人员产生任何实质性内容。 id name lft rgt depth 37 2nd level board 3 6 15 0 40 3rd level board 1 13 14 1 41 3rd level board 2 9 12 1 42 3rd level board 3 7 8 1 id name lft rgt depth 35 Top level board 1 16 0 37 2nd level board 3 6 15 1 38 2nd level board 2 4 5 1 39 2nd level board 1 2 3 1
<?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();
?>
<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>
-- 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);