[英]mysql hierarchy self-join, retrieve all subcategories
我的表categories
包含以下各列:
category_id
category_name
parent_id
我需要获取给定主要类别在所有级别上所有子类别的列表,因此,例如,如果我给出某些lvl 3类别的ID,我将获得所有lvl 4、5、6 ...类别的子类别的列表那一个3级类别。
无需保留层次结构,只需保留一个简单列表即可。
我首先想到只通过几个连接和子查询来完成此操作,但是我认为类别之后会更深,所以这不是走的路。
由于我刚开始使用SQL,所以我仍然不知道如何编写递归查询,因此这将是一个很好的帮助和学习资料。
Please Note
阅读底部的Please Note
。 好的,您回来了。
为类似递归的层次结构检索创建存储过程。
请注意,您不希望按级别进行操作,但是可以轻松完成此操作。
create table category
( category_id int not null auto_increment primary key,
category_name varchar(40) not null,
parent_id int null, -- index on this column not a shabby idea
unique key (category_name)
);
insert category(category_name,parent_id) values ('car',null),('food',null); -- 1,2
insert category(category_name,parent_id) values ('ford',1),('chevy',1),('fruit',2); -- 3,4,5
insert category(category_name,parent_id) values ('economy',3),('escort',6),('exhaust',7); -- 6,7,8
insert category(category_name,parent_id) values ('chassis',7),('loud',8),('banana',5); -- 9,10,11
-- ok granted I could have explicity inserted category_id to make it more obvious
-- drop procedure showHierarchyBelow;
delimiter $$
create procedure showHierarchyBelow
(
catname varchar(40)
)
BEGIN
-- deleteMe parameter means i am anywhere in hierarchy of role
-- and i want me and all my offspring deleted (no orphaning of children or theirs)
declare bDoneYet boolean default false;
declare working_on int;
declare theCount int;
declare findFirst int;
select ifnull(category_id,0) into findFirst from category where category_name=catname;
CREATE TABLE xx_RecursishHelper_xx
( -- it's recurshish, not recursive
category_id int not null,
processed int not null
);
if isnull(findFirst) then
set findFirst=0;
end if;
insert into xx_RecursishHelper_xx (category_id,processed) select findFirst,0;
if (findFirst=0) then
set bDoneYet=true;
else
set bDoneYet=false;
end if;
while (!bDoneYet) do
-- I am not proud of this next line, but oh well
select count(*) into theCount from xx_RecursishHelper_xx where processed=0;
if (theCount=0) then
-- found em all
set bDoneYet=true;
else
-- one not processed yet, insert its children for processing
SELECT category_id INTO working_on FROM xx_RecursishHelper_xx where processed=0 limit 1;
insert into xx_RecursishHelper_xx (category_id,processed)
select category_id,0 from category
where parent_id=working_on;
-- mark the one we "processed for children" as processed
update xx_RecursishHelper_xx set processed=1 where category_id=working_on;
end if;
end while;
delete from xx_RecursishHelper_xx where category_id=findFirst;
select x.category_id,c.category_name
from xx_RecursishHelper_xx x
join category c
on c.category_id=x.category_id;
drop table xx_RecursishHelper_xx;
END
$$
call showHierarchyBelow('food');
+-------------+---------------+
| category_id | category_name |
+-------------+---------------+
| 5 | fruit |
| 11 | banana |
+-------------+---------------+
call showHierarchyBelow('car');
+-------------+---------------+
| category_id | category_name |
+-------------+---------------+
| 3 | ford |
| 4 | chevy |
| 6 | economy |
| 7 | escort |
| 8 | exhaust |
| 9 | chassis |
| 10 | loud |
+-------------+---------------+
call showHierarchyBelow('ford');
+-------------+---------------+
| category_id | category_name |
+-------------+---------------+
| 6 | economy |
| 7 | escort |
| 8 | exhaust |
| 9 | chassis |
| 10 | loud |
+-------------+---------------+
call showHierarchyBelow('xxx');
-- no rows
请注意,我只是几个月前根据您的需要修改了我的答案 。
以上仅用于说明目的。 在现实世界中,我永远不会在存储的proc中创建表。 DDL开销很大。 相反,我将使用具有会话概念的预先存在的非临时表。 并将其清理干净以完成会话。 因此,不要将上述内容当作一个稻草人,等着您使它更具性能。 询问是否令人困惑。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.