2011-03-30 64 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);