有没有办法在不使用递归的情况下从MySQL中获取嵌套数据?

时间:2011-12-01 12:03:23

标签: php 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查询来处理。也许需要更改表结构以适应这种情况 - 如果是这样的话?

4 个答案:

答案 0 :(得分: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

这样,您只需查询一次数据库,但可以使用更多的服务器ram。

答案 1 :(得分:2)

如果您要获取整棵树而您不能或不想更改表格结构,请查看https://stackoverflow.com/a/8325451/4833

答案 2 :(得分:1)

这可以在sql查询中完成,看看这个资源解释了查询中的递归

http://www.artfulsoftware.com/mysqlbook/sampler/mysqled1ch20.html

答案 3 :(得分: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;