【问题标题】:mySQL transitive closure tablemySQL 传递闭包表
【发布时间】:2010-11-22 04:57:57
【问题描述】:

我在 SQL Server 中使用了一些代码,用于从另一个只有直接父/子关系的表生成一个闭包表,我可以对此运行非常简单的查询来确定沿袭。现在我需要在 mySQL 中完成所有这些操作,但是我无法通过递归查询来生成闭包表...

我原来的 SQL 服务器查询是

WHILE @@ROWCOUNT>0
INSERT INTO [ClosureTable] ([Ancestor], [Descendent])
SELECT distinct [Parent],[tc].[Descendent] 
FROM 
    [RelationshipTable] 
INNER JOIN [ClosureTable] as tc
    ON [Child]COLLATE DATABASE_DEFAULT = 
                      [tc].[Ancestor]COLLATE DATABASE_DEFAULT
LEFT OUTER JOIN [ClosureTable] As tc2
    ON [Parent]COLLATE DATABASE_DEFAULT =
                      [tc2].[Ancestor] COLLATE DATABASE_DEFAULT 
    AND [tc].[Descendent]COLLATE DATABASE_DEFAULT =
                      [tc2].[Descendent]COLLATE DATABASE_DEFAULT

我的第一个问题是找到@@ROWCOUNT 的替代品...但也许递归查询在 mySQL 中完全不同?我还查看了Bill Karwin's presentation

PS。由于性能问题,我需要“COLLATE DATABASE_DEFAULT”。

谢谢。

【问题讨论】:

    标签: mysql closures recursive-query transitive-closure-table


    【解决方案1】:

    我知道这是旧的,但我觉得你仍然需要其他人的答案,这是我如何从标准邻接表生成我的闭包表:

    mysql_query('TRUNCATE fec_categories_relations');
    
    function rebuild_tree($parent)
    {
        // get all children of this node
        $result = mysql_query('SELECT c.categories_id, c.parent_id, cd.categories_name FROM fec_categories c
                                INNER JOIN fec_categories_description cd ON c.categories_id = cd.categories_id
                                WHERE c.parent_id = "'.$parent.'"
                                AND      cd.language_id = 1
                                ORDER BY cd.categories_name');
    
        // loop through 
        while ($row = mysql_fetch_array($result))
        {       
            $update_sql = " INSERT fec_categories_relations (ancestor, descendant, length)
                            SELECT ancestor, {$row['categories_id']}, length+1
                            FROM fec_categories_relations
                            WHERE descendant = {$row['parent_id']}
                            UNION ALL SELECT {$row['categories_id']},{$row['categories_id']}, 0";
    
            mysql_query($update_sql);
    
            echo '<li>' . $update_sql . "</li>";
    
            rebuild_tree($row['categories_id']);
        }
    }
    
    rebuild_tree(0);
    

    【讨论】:

    • 在延迟响应的传统中...谢谢。我会试一试。我实际上最终解决了它,只是没有机会一起得到答案。我会在接下来的几天里检查你的答案,所以我可以关闭这个问题——尽管这一切都是在数据库中完成的,而不是在 php.ini 中。谢谢。
    【解决方案2】:

    不确定我是否理解您的确切问题 - 我认为它是从邻接列表表中递归生成树 - 如果是这样,以下内容可能会有所帮助,但它不是递归的(真可惜!!)

    drop table if exists employees;
    create table employees
    (
    emp_id smallint unsigned not null auto_increment primary key,
    name varchar(255) not null,
    boss_id smallint unsigned null,
    key (boss_id)
    )
    engine = innodb;
    
    insert into employees (name, boss_id) values
    ('f00',null), 
      ('ali later',1), 
      ('megan fox',1), 
       ('jessica alba',3), 
       ('eva longoria',3), 
       ('keira knightley',5), 
          ('liv tyler',6), 
            ('sophie marceau',6);
    
    
    drop procedure if exists employees_hier;
    
    delimiter #
    
    create procedure employees_hier
    (
    in p_emp_id smallint unsigned
    )
    begin
    
    declare v_done tinyint unsigned default(0);
    declare v_dpth smallint unsigned default(0);
    
    create temporary table hier(
     boss_id smallint unsigned, 
     emp_id smallint unsigned, 
     depth smallint unsigned
    )engine = memory;
    
    insert into hier select boss_id, emp_id, v_dpth from employees where emp_id = p_emp_id;
    
    /* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
    
    create temporary table emps engine=memory select * from hier;
    
    while not v_done do
    
     if exists( select 1 from employees e inner join hier on e.boss_id = hier.emp_id and hier.depth = v_dpth) then
    
      insert into hier select e.boss_id, e.emp_id, v_dpth + 1 
       from employees e inner join emps on e.boss_id = emps.emp_id and emps.depth = v_dpth;
    
      set v_dpth = v_dpth + 1;   
    
      truncate table emps;
      insert into emps select * from hier where depth = v_dpth;
    
     else
      set v_done = 1;
     end if;
    
    end while;
    
    select 
     e.emp_id,
     e.name as emp_name,
     p.emp_id as boss_emp_id,
     p.name as boss_name,
     hier.depth
    from 
     hier
    inner join employees e on hier.emp_id = e.emp_id
    left outer join employees p on hier.boss_id = p.emp_id;
    
    drop temporary table if exists hier;
    drop temporary table if exists emps;
    
    end #
    
    delimiter ;
    
    -- call this sproc from your php
    
    call employees_hier(1);
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-30
    相关资源
    最近更新 更多