mySQL transitive closure table
I have some code I've been using in SQL Server to generate a closure table from another table that has just the direct parent/child relationships, I can run very simple queries against this to determine lineage. Now I am needing to do all this in mySQL, but I am having trouble with the recursive querying to generate the closure table...
My original SQL server query is
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 OUTE开发者_如何学CR 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
My first problem is finding a substiture for @@ROWCOUNT... but perhaps recursive queries are completely different in mySQL? I've also checked out Bill Karwin's presentation
PS. The "COLLATE DATABASE_DEFAULT" was something I needed due to performance issues..
Thanks.
I know this is old, but I feel you still need an answer on this for others looking, here is how I generated my closure table from my standard adjacency table:
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);
not sure if i understood what your exact problem is - i think it's around recursively generating a tree from an adjacency list table - if so, the following may help but it's not recursive (what a shame !!)
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);
精彩评论