2011-04-16 214 views
1

我試圖圍繞查詢具有層次結構類別數據(用於cms)的表進行打包,該表還與我的帖子數據和多對多類型關係綁定我的post2cat表。具體來說,我的問題是,如何獲取屬於某個特定類別ID的任何子類別的所有帖子(不限於直接後裔,但可以是n級)?這裏是我的表:需要分層Mysql查詢的幫助

'類' 表:

+-----------+-------------+------+-----+---------+----------------+ 
| Field  | Type  | Null | Key | Default | Extra   | 
+-----------+-------------+------+-----+---------+----------------+ 
| id  | int(11)  | NO | PRI | NULL | auto_increment | 
| name  | varchar(45) | YES |  | NULL |    | 
| parent_id | int(11)  | YES | MUL | 0  |    | 
+-----------+-------------+------+-----+---------+----------------+ 
3 rows in set (0.00 sec) 

'post2cat' 表:

+---------+---------+------+-----+---------+-------+ 
| Field | Type | Null | Key | Default | Extra | 
+---------+---------+------+-----+---------+-------+ 
| post_id | int(11) | NO | MUL | NULL |  | 
| cat_id | int(11) | NO | MUL | NULL |  | 
+---------+---------+------+-----+---------+-------+ 
2 rows in set (0.00 sec) 

'帖子' 表:

+---------------+--------------+------+-----+---------+----------------+ 
| Field   | Type   | Null | Key | Default | Extra   | 
+---------------+--------------+------+-----+---------+----------------+ 
| id   | int(11)  | NO | PRI | NULL | auto_increment | 
| title   | varchar(256) | NO |  | NULL |    | 
| content  | text   | NO |  | NULL |    | 
| end_date  | datetime  | NO |  | NULL |    | 
| format_id  | int(11)  | NO |  | NULL |    | 
| featured  | int(1)  | NO |  | NULL |    | 
| status  | int(3)  | NO |  | NULL |    | 
| publish_date | datetime  | NO |  | NULL |    | 
| date_created | datetime  | NO |  | NULL |    | 
| date_modified | datetime  | NO |  | NULL |    | 
+---------------+--------------+------+-----+---------+----------------+ 

POST_ID名稱描述

post2cat

+1

你'類別'表是一個鄰接表模型。使用MySQL會話變量來遍歷樹。我關於SQL中的分層數據的問題涵蓋了更多關於這個一般問題的內容:http://stackoverflow.com/questions/4048151/what-are-the-options-for-storing-hierarchical-data-in-a-relational-數據庫 – orangepips 2011-04-19 14:38:08

回答

4

這是樹取在MySQL中使用SQL只是一個任意深度ñ(參見:Managing Hierarchical Data in MySQLHierarchical Queries in MySQL)是一個挑戰。

如果您正在處理一個小數據集,從數據庫中選擇所有類別,然後使用腳本語言構建樹是最有意義的。然後可以遍歷該樹來查找父節點和父節點的子節點,然後可以使用連接來查詢數據庫中的帖子。

+0

爲您提供的第一個鏈接+1。但是,您的評論與鏈接的內容不一致。您似乎不需要使用腳本語言,而是使用嵌套集模型。 – VinnyD 2011-04-18 17:14:25

+0

在MySQL中,爲了從鄰接列表模型中檢索樹,您需要知道深度。第二個環節是試圖繞過這個環節。 – gcorne 2011-04-19 00:40:42

1

也許有更好的方法,但這裏有一個想法:

CREATE VIEW subcategories AS 
(SELECT 0  AS level 
     , cat_0.id AS cat_id 
     , cat_0.id AS subcat_id 
    FROM categories cat_0 

    UNION ALL 

    SELECT 1  AS level 
     , cat_0.id AS cat_id 
     , cat_1.id AS subcat_id 
    FROM categories cat_0 
    JOIN categories cat_1 
     ON cat_0.id = cat_1.parent_id 

    UNION ALL 

    SELECT 2  AS level 
     , cat_0.id AS cat_id 
     , cat_2.id AS subcat_id 
    FROM categories cat_0 
    JOIN categories cat_1 
     ON cat_0.id = cat_1.parent_id 
    JOIN categories cat_2 
     ON cat_1.id = cat_2.parent_id 

    UNION ALL 

    SELECT 3  AS level 
     , cat_0.id AS cat_id 
     , cat_3.id AS subcat_id 
    FROM categories cat_0 
    JOIN categories cat_1 
     ON cat_0.id = cat_1.parent_id 
    JOIN categories cat_2 
     ON cat_1.id = cat_2.parent_id 
    JOIN categories cat_3 
     ON cat_2.id = cat_3.parent_id 
) ; 

然後,用上面的有:

SELECT sub.subcat_id 
    , sub.level 
    , p.post_id 
    , p.title 
FROM subcategories sub 
    JOIN post2cat p2c 
    ON sub.subcat_id = p2c.cat_id 
    JOIN posts p 
    ON p2c.post_id = p.id 
WHERE sub.cat_id = CAT_ID  <--- the category id you want to search for 
0

下面的存儲過程應該提供一個良好的起點,或者提供一些靈感。

希望它能幫助:)

調用示例

您可以從PHP調用存儲過程如下:

$result = $conn->query(sprintf("call category_hier(%d)", 1)); 

或從mysql命令行:

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; 

insert into categories (name, parent_cat_id) values 
('Location',null), 
('Color',null), 
    ('USA',1), 
     ('Illinois',3), 
     ('Chicago',3), 
    ('Black',2), 
    ('Red',2); 


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 tmp on c.parent_cat_id = tmp.cat_id and tmp.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 category_hier(1); 

call category_hier(2);