2011-03-30 59 views
1

我有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

在提出新问题之前,请检查您过去的答案;成为SO社区感谢的一员。 ;-) – 2011-03-30 12:28:02

回答

0

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

最简单的建模方法是有三列:id,titleparentparent是引用当前表的“外键”。如果parent设置为空,则该类别为“顶级”;如果不是(并且被设置为表格中的现存记录),则它是一个子类别。

0

没有阅读文章,但我看了你的SQL。一个好的模式通常是跟踪一个项目的父项而不是子项目。我认为可以摆脱一些不必要的迭代。

这和Composite Pattern是一样的想法。

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 
1

有几种可能性。您可以在行中存储对每个子类别的父项的引用,就像syroin建议的一样。您也可以使用修改的预定义树遍历。它更复杂,但许多树操作在SQL中变得更简单。作为第三种选择,一些系统(例如CakePHP的TreeBehaviour)将它们结合起来。他们存储leftright字段用于MPTT和parent字段以便于重组。

看看Storing Hierarchical Data in a Database

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);