开发者

what is the best method to make unlimited sub catgories

I have this article about the method, but this method is not really good because its loop the sql query millions times.

Anyone knows any other method using the same MySQL structure?

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;


There are four widely used methods to do this:

  • Adjacency list
  • Nested sets
  • Materialized paths
  • Transitive closure of an adjacency list

Adjacency list (the one in your post) is the most convenient however it requires a support for recursive queries which MySQL lacks, though it can be emulated to some extent:

  • Hierarchical queries in MySQL


There are several possibilities. You can store a reference to the parent of each subcategory in the row, just like syroin suggested. You can also use Modified Preorder Tree Traversal. It's more complicated, but many tree operations become much simpler to do in SQL. As a third alternative, some systems (e.g. CakePHP's TreeBehaviour) combine them both. They store a left and right field for MPTT and a parent field for easy reorganisation.

Take a look at Storing Hierarchical Data in a Database


  • Managing Hierarchical Data in MySQL http://dev.mysql.com

  • MySQL Categories and Subcategories Table Structure http://stackoverflow.com


This is more of a database design question than anything else, I believe. You're asking about how to create "Unlimited subcategories," but what you want, if I understand you correctly, is a normal tree structure (parent, child, grandchild).

The easiest way to model this is to have three columns: id, title, and parent. parent is a "foreign key" referencing the current table. If parent is set to null, the category is "top level;" if it isn't (and is set to an extant record in the table) it is a subcategory.


didn't read the article but I looked over your sql. A good pattern to do this generally is to keep track of an item's parent rather than it's sub-categories. I think that can get rid of some unnecessary iterations.

It's the same idea as the Composite Pattern.


You can use this structure and create an additional column which stores all the subcategories. With this column you will need only one query for most tasks I can think of.

Example:

a
    b
        d
    c

This tree would be represented as:

id  title   parent  parents
1   a       0       0
2   b       1       0,1
3   c       1       0,1
4   d       2       0,1,2


You can stick with the adjacency list implementation and do it with a single call from your app layer (php) to mysql if you use a stored procedure. The following stored procedure is iterative vs. recursive but still provides good performance in most cases and the adjacency list implementation has a multitude of advantages over other methods especially when it comes to maintaining the hierarchy:

Generating Depth based tree from Hierarchical Data in MySQL (no CTEs)

You can call the stored procedure from php as follows:

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

Script

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜