什么是制作无限子类别的最佳方法

时间:2011-03-30 12:17:51

标签: php mysql

我有关于该方法的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;

7 个答案:

答案 0 :(得分:3)

有四种广泛使用的方法可以做到这一点:

邻接列表(您帖子中的那个)是最方便的,但它需要支持MySQL缺少的递归查询,尽管可以在某种程度上模拟:

答案 1 :(得分:1)

有几种可能性。您可以存储对行中每个子类别的父级的引用,就像syroin建议的那样。您还可以使用Modified Preorder Tree Traversal。它更复杂,但许多树操作在SQL中变得更加简单。作为第三种选择,一些系统(例如CakePHP的TreeBehaviour)将它们两者结合起来。他们为MPTT存储leftright字段,并为parent字段存储以便于重组。

查看Storing Hierarchical Data in a Database

答案 2 :(得分:1)

答案 3 :(得分:0)

我相信,这更像是一个数据库设计问题。您正在询问如何创建“无限子类别”,但如果我理解正确的话,您想要的是正常的树结构(父,子,孙)。

对此进行建模的最简单方法是使用三列:idtitleparentparent是引用当前表的“外键”。如果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);