2011-12-01 72 views
2

因此,让我们说,我有一个存储在MySQL表像这样的所有导航项目的菜单系统:有没有办法在不使用递归的情况下从MySQL获取嵌套数据?

Table: Menu 
------------------------------------------------------- 
| id | title  | url     | parent_id | 
------------------------------------------------------- 
| 1 | Home  | /home     | 0   | 
| 2 | About  | /about    | 0   | 
| 3 | History | /about/history  | 2   | 
| 4 | Location | /about/location  | 2   | 
| 5 | Staff  | /about/staff   | 2   | 
| 6 | Articles | /blog     | 0   | 
| 7 | Archive | /blog/archive   | 6   | 
| 8 | Tags  | /blog/tags   | 6   | 
| 9 | Tag Name 1 | /blog/tags/tag-name-1 | 8   | 
| 10 | Tag Name 2 | /blog/tags/tag-name-2 | 8   | 
------------------------------------------------------- 

正如你可以看到这个表是唯一的并发症是自引用列很简单parent_id,它定义了菜单应该如何嵌套。

因此,这将产生以下菜单:

- Home 
- About 
    - History 
    - Location 
    - Staff 
- Articles 
    - Archive 
    - Tags 
     - Tag Name 1 
     - Tag Name 2 

是否有一种方式来获得从上述表这种结构无需在PHP中使用递归函数的(但它可能是Python和Java或任何其他语言)每次迭代查询数据库?

理想情况下,这可以用一个MySQL查询来处理。为了适应这种情况,可能需要改变表格结构 - 如果是这样的话?

+0

是否有一个特定的原因,阻止你使用递归函数?这是这样的数据结构最合理的解决方案。 –

+0

@TillHelgeHelwig该代码目前使用递归函数,但我想减少菜单呈现期间发生的数据库命中数。所以我想我有两个选择。缓存菜单或尝试在单个MySQL查询中获取数据。为了进行调试,也可以使用MySQL以外的嵌套结构。 – Treffynnon

+0

是 - 看到这里http://stackoverflow.com/questions/5291054/hierarchical-sql-problem/5291159#5291159 –

回答

4

您可以将所有内容全部拉出,然后在PHP中递归处理。这样你节省了一些查询时间,但获得一些脚本时间。

我会做这样的事情:

Get all data, ordered by parent id 
Put row into $data[$parent_id][] 

define function to build menu, takes one param which is id 
get $data[$id] and work with that array, building the array. 

while looping through the items, check if size of $data[current-item-id] > 0 
if so, call above function with 0 as param 

这样,你只查询数据库一次,但用多一点的服务器内存。

0

MySQL没有默认的功能来做到这一点。

你可以用procedure循环来获得你想要的数据结果,或者创建一个函数并在你的sql select中使用。

无论如何,你会使用循环。

例子:

DROP PROCEDURE IF EXISTS famsubtree; 
DELIMITER go 
CREATE PROCEDURE famsubtree(root INT) 
BEGIN 
    DROP TABLE IF EXISTS famsubtree; 
    CREATE TABLE famsubtree 
    SELECT childID, parentID, 0 AS level 
    FROM familytree 
    WHERE parentID = root; 
    ALTER TABLE famsubtree ADD PRIMARY KEY(childID,parentID); 
    REPEAT 
    INSERT IGNORE INTO famsubtree 
     SELECT f.childID, f.parentID, s.level+1 
     FROM familytree AS f 
     JOIN famsubtree AS s ON f.parentID = s.childID; 
    UNTIL Row_Count() = 0 END REPEAT; 
E ND ; 
go 
DELIMITER ; 

和使用查询:

call famsubtree(1);  -- from the root you can see forever 
SELECT Concat(Space(level),parentID) AS Parent, Group_Concat(childID ORDER BY childID) AS Child 
FROM famsubtree 
GROUP BY parentID; 
相关问题