我有关于该方法的this article,但这种方法并不是很好,因为它循环了sql查询数百万次。
任何人都知道使用相同MySQL结构的任何其他方法吗?
CREATE TABLE IF NOT EXISTS `jooria_categories` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` text COLLATE utf8_bin NOT NULL,
`sub` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1;
答案 0 :(得分:3)
有四种广泛使用的方法可以做到这一点:
邻接列表(您帖子中的那个)是最方便的,但它需要支持MySQL
缺少的递归查询,尽管可以在某种程度上模拟:
答案 1 :(得分:1)
有几种可能性。您可以存储对行中每个子类别的父级的引用,就像syroin建议的那样。您还可以使用Modified Preorder Tree Traversal。它更复杂,但许多树操作在SQL中变得更加简单。作为第三种选择,一些系统(例如CakePHP的TreeBehaviour)将它们两者结合起来。他们为MPTT存储left
和right
字段,并为parent
字段存储以便于重组。
答案 2 :(得分:1)
答案 3 :(得分:0)
我相信,这更像是一个数据库设计问题。您正在询问如何创建“无限子类别”,但如果我理解正确的话,您想要的是正常的树结构(父,子,孙)。
对此进行建模的最简单方法是使用三列:id
,title
和parent
。 parent
是引用当前表的“外键”。如果parent
设置为null,则该类别为“顶级”;如果不是(并且设置为表中的现存记录),则它是子类别。
答案 4 :(得分:0)
没看过文章,但我查看了你的sql。这样做的一个好模式通常是跟踪项目的父项而不是它的子类别。我认为可以摆脱一些不必要的迭代。
这与Composite Pattern的想法相同。
答案 5 :(得分:0)
您可以使用此结构并创建一个存储所有子类别的附加列。使用此列,您只需要一个查询即可查看我能想到的大多数任务。
示例:
a
b
d
c
此树将表示为:
id title parent parents
1 a 0 0
2 b 1 0,1
3 c 1 0,1
4 d 2 0,1,2
答案 6 :(得分:0)
如果使用存储过程,您可以坚持使用邻接列表实现,并通过从应用层(php)到mysql的单个调用来执行此操作。以下存储过程是迭代与递归的,但在大多数情况下仍然提供良好的性能,并且邻接列表实现与其他方法相比具有许多优点,尤其是在维护层次结构时:
Generating Depth based tree from Hierarchical Data in MySQL (no CTEs)
您可以按如下方式从php调用存储过程:
$result = $conn->query(sprintf("call category_hier(%d)", 1));
mysql> call category_hier(1);
+--------+---------------+---------------+----------------------+-------+
| cat_id | category_name | parent_cat_id | parent_category_name | depth |
+--------+---------------+---------------+----------------------+-------+
| 1 | Location | NULL | NULL | 0 |
| 3 | USA | 1 | Location | 1 |
| 4 | Illinois | 3 | USA | 2 |
| 5 | Chicago | 3 | USA | 2 |
+--------+---------------+---------------+----------------------+-------+
4 rows in set (0.00 sec)
<强>脚本强>
drop table if exists categories;
create table categories
(
cat_id smallint unsigned not null auto_increment primary key,
name varchar(255) not null,
parent_cat_id smallint unsigned null,
key (parent_cat_id)
)
engine = innodb;
-- TEST DATA
insert into categories (name, parent_cat_id) values
('Location',null),
('Color',null),
('USA',1),
('Illinois',3),
('Chicago',3),
('Black',2),
('Red',2);
-- STORED PROCEDURES
drop procedure if exists category_hier;
delimiter #
create procedure category_hier
(
in p_cat_id smallint unsigned
)
begin
declare v_done tinyint unsigned default 0;
declare v_depth smallint unsigned default 0;
create temporary table hier(
parent_cat_id smallint unsigned,
cat_id smallint unsigned,
depth smallint unsigned default 0
)engine = memory;
insert into hier select parent_cat_id, cat_id, v_depth from categories where cat_id = p_cat_id;
create temporary table tmp engine=memory select * from hier;
/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */
while not v_done do
if exists( select 1 from categories c
inner join hier on c.parent_cat_id = hier.cat_id and hier.depth = v_depth) then
insert into hier select c.parent_cat_id, c.cat_id, v_depth + 1 from categories c
inner join tmp on c.parent_cat_id = tmp.cat_id and tmp.depth = v_depth;
set v_depth = v_depth + 1;
truncate table tmp;
insert into tmp select * from hier where depth = v_depth;
else
set v_done = 1;
end if;
end while;
select
c.cat_id,
c.name as category_name,
p.cat_id as parent_cat_id,
p.name as parent_category_name,
hier.depth
from
hier
inner join categories c on hier.cat_id = c.cat_id
left outer join categories p on hier.parent_cat_id = p.cat_id
order by
hier.depth;
drop temporary table if exists hier;
drop temporary table if exists tmp;
end #
delimiter ;
-- call from php
call category_hier(1);
call category_hier(2);