Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 以有效且简单的方式实现层级、父/子关系_Php_Mysql_Sql_Algorithm_Hierarchical Data - Fatal编程技术网

Php 以有效且简单的方式实现层级、父/子关系

Php 以有效且简单的方式实现层级、父/子关系,php,mysql,sql,algorithm,hierarchical-data,Php,Mysql,Sql,Algorithm,Hierarchical Data,我有一张像这样的桌子 create table site ( site_Id int(5), parent_Id int(5), site_desc varchar2(100) ); 字段的重要性: 站点Id:站点的Id 父Id:站点的父Id 站点描述:虽然与问题无关,但有站点描述 要求是,如果我有一个站点id作为输入,并且我需要在站点下方标记所有id。 例如: A / \ B

我有一张像这样的桌子

create table site
(
site_Id int(5),
parent_Id int(5),
site_desc varchar2(100)
);
字段的重要性:

  • 站点Id:站点的Id
  • 父Id:站点的父Id
  • 站点描述:虽然与问题无关,但有站点描述
要求是,如果我有一个站点id作为输入,并且我需要在站点下方标记所有id。 例如:

                    A
                   / \
                  B   C
                / | \ /\
               D  E F G H
              /\
             I  J
所有节点都是站点Id

该表包含如下数据:

Site_id  | Parent_ID  |  site_desc
_________|____________|___________
 A       |   -1       |   
 B       |    A       |
 C       |    A       |
 D       |    B       |
 E       |    B       |
 F       |    B       |
 I       |    D       |
 J       |    D       |
$nodes  = array( $parent_id );

$cursor = SQLQuery("SELECT * FROM site WHERE Site_ID > ? "
        .  "ORDER BY Parent_ID, Site_Id ;", $parent_id);

while ($tuple = SQLFetchTuple($cursor))
    if (in_array($tuple['Parent_ID'], $nodes))
        $nodes[] = $tuple['Site_Id'];
SQLFree($cursor);

// The first node is the global parent, and may be array_shift'ed away
    // if desired.
select site_id
from site
where site_id in (
    select site_id 
    from site 
    where parent_id = 1
)
or site_id in (
    select grandchild.site_id 
    from site grandchild, site child 
    where grandchild.parent_id = child.site_id 
    and child.parent_id = 1
)
or site_id in (
    select greatgrandchild.site_id 
    from site greatgrandchild, site grandchild, site child 
    where greatgrandchild.parent_id = grandchild.site_id 
    and grandchild.parent_id = child.site_id 
    and child.parent_id = 1
)

A是B和C的父代,依此类推

如果B是给定的输入,那么查询需要获取D、E、I、F、J

目前,它是通过循环中的多个查询来实现的,但我想用最少数量的查询来实现这一点

我目前正在做的是:

否决票

算法如下所示:

Initially create a data set object which you will populate, by fetching data from the data base. 
Create a method which takes the parent id as parameter and returns its child nodes if present, and returns -1, if it doesnt have a child. 
Step1: Fetch all the rows, which doesn't have a parent(root) node. 
Step2: Iterate through this result. For example if prod1 and prod2 are the initial returned nodes, in the resultset. 
Iterating this RS we get prod1, and we insert a row in our DataSET obj. 
Then we send the id of prod1 to getCHILD method, to get its child, and then again we iterate the returned resultset, and again call the getCHILD method, till we dont get the lowest node.
我需要在数据模型约束范围内使用最佳优化技术。如果您有任何建议,请随时回答。
请提出任何建议。
提前感谢。

如果您希望能够在单个查询中执行此操作,请查看嵌套集模型:


另一种选择是在链接表中包含所有关系。所以每个网站都会有一个链接到它的父母,祖父母,等等。每个关系都是明确的。然后您只需查询该链接表即可获得所有子体。

如果您不经常更新
站点
表,您可以使用以下策略:

create table site
(
site_Id int(5),
parent_Id int(5),
site_desc varchar2(100),
parents_path varchar(X)
);
parents\u path
等于从根节点到所选节点的路径。例如,对于leaf
J
,它应该是
|A | B | D |

优点: -您将需要单个查询来获得结果

缺点: -更新期间进行更多查询(但您可以明智地进行更新)


希望它能有所帮助。首先,我推荐一种不同的存储树的方法:闭包表。若你们想更多地了解它,你们会发现这本书很有趣

也就是说。在我看来,生成这种结构的最简单方法是:

我希望您在阅读JavaScript代码时没有问题。我之所以使用它,是因为在JavaScript中创建未分类的对象看起来并没有那么黑客。通过使用多维数组,可以在不依赖对象(或引用)的情况下实现相同的功能,但看起来有点混乱

以下是算法的作用:

  • 我们循环遍历节点列表,一次
  • 如果节点的父节点未退出,则在数组中创建占位符
  • 若节点并没有父节点,则将其放置在根节点列表中
  • 如果节点在数组中没有占位符,则将创建占位符
  • 节点中的值将指定给占位符
  • 节点已注册到父节点(如果有父节点)

就是这样。基本上,您可以生成两个列表:包含所有节点,并且仅包含根节点。

其他人已经提出了如何实现这一点,只是对表做了一些修改 结构

如果您不想修改结构(即使这是最好的),那么您可以这样做 像这样:

Site_id  | Parent_ID  |  site_desc
_________|____________|___________
 A       |   -1       |   
 B       |    A       |
 C       |    A       |
 D       |    B       |
 E       |    B       |
 F       |    B       |
 I       |    D       |
 J       |    D       |
$nodes  = array( $parent_id );

$cursor = SQLQuery("SELECT * FROM site WHERE Site_ID > ? "
        .  "ORDER BY Parent_ID, Site_Id ;", $parent_id);

while ($tuple = SQLFetchTuple($cursor))
    if (in_array($tuple['Parent_ID'], $nodes))
        $nodes[] = $tuple['Site_Id'];
SQLFree($cursor);

// The first node is the global parent, and may be array_shift'ed away
    // if desired.
select site_id
from site
where site_id in (
    select site_id 
    from site 
    where parent_id = 1
)
or site_id in (
    select grandchild.site_id 
    from site grandchild, site child 
    where grandchild.parent_id = child.site_id 
    and child.parent_id = 1
)
or site_id in (
    select greatgrandchild.site_id 
    from site greatgrandchild, site grandchild, site child 
    where greatgrandchild.parent_id = grandchild.site_id 
    and grandchild.parent_id = child.site_id 
    and child.parent_id = 1
)
  • 按父项ID、站点ID从站点订单中选择*
通常可以安全地假设,一旦分配,ID不会改变;如果ID 不要随意移动,也就是说,节点C没有移动到节点B下,那么它就会移动 如果子节点的ID始终高于其父节点,则为true,并且排序 以上将保证所有的父母在他们的孩子出生之前就被带走

以下是假设:

- we prefer not to change the table layout
- we never change the IDs once assigned
- we never reorder the tree, moving IDs around
因此,可以在内存中创建树(甚至减少查询) 本身添加了一个WHERE Site_ID>=B)

通过的第一个节点将是B,并将被放入树中

所有后续节点都可以存储在其父节点_ID-th节点中,该节点肯定已被删除 以前加载过

在Python中,这将非常顺利(您可以直接修改父节点)

“获取B的所有后代”请求可以在PHP中得到如下回答:

Site_id  | Parent_ID  |  site_desc
_________|____________|___________
 A       |   -1       |   
 B       |    A       |
 C       |    A       |
 D       |    B       |
 E       |    B       |
 F       |    B       |
 I       |    D       |
 J       |    D       |
$nodes  = array( $parent_id );

$cursor = SQLQuery("SELECT * FROM site WHERE Site_ID > ? "
        .  "ORDER BY Parent_ID, Site_Id ;", $parent_id);

while ($tuple = SQLFetchTuple($cursor))
    if (in_array($tuple['Parent_ID'], $nodes))
        $nodes[] = $tuple['Site_Id'];
SQLFree($cursor);

// The first node is the global parent, and may be array_shift'ed away
    // if desired.
select site_id
from site
where site_id in (
    select site_id 
    from site 
    where parent_id = 1
)
or site_id in (
    select grandchild.site_id 
    from site grandchild, site child 
    where grandchild.parent_id = child.site_id 
    and child.parent_id = 1
)
or site_id in (
    select greatgrandchild.site_id 
    from site greatgrandchild, site grandchild, site child 
    where greatgrandchild.parent_id = grandchild.site_id 
    and grandchild.parent_id = child.site_id 
    and child.parent_id = 1
)
另一种方式
蛮力

另一种可能性是在另一个数据库中递归地存储关系的“后代” 表:

并重复插入,直到插入的行数等于零(或总行数) 子体中的行停止增加;查询表大小在大多数数据库中非常快)

此时,您将存储所有一级关系。现在:

INSERT IGNORE INTO descendants SELECT s1.node, s2.of FROM
           descendants AS s1 JOIN descendants AS s2 ON (s1.of = s2.node);
…再次执行,直到子体停止增加(需要的插入次数等于 最大级别数)。联接的总数将是级别数的两倍

现在,如果您想获得节点16的所有后代,只需查询

SELECT node FROM descendants WHERE of = 16;
昨天,我遇到了一个与您描述的问题完全相关的问题:在给定的邻接列表中,您希望获得特定父节点的所有子节点——也许是在一个一维数组中,您可以轻松地进行迭代

只需对数据库进行一次调用就可以做到这一点,但有一个问题:必须返回表中的所有行。MySQL不支持递归查询,因此您实际上必须在应用程序代码中执行
SELECT
ing

我只想重申我上面链接的答案,但基本上如果您以如下格式返回结果集(可能来自
PDOStatement->fetchAll(PDO::FETCH_ASSOC)
或其他方法):

Array
(
    [0] => Array
    (
        [site_id] => A
        [parent_id] => -1
        [site_desc] => testtext
    )
    [1] => Array
    (
        [site_id] => B
        [parent_id] => A
        [site_desc] => testtext
    )
    [2] => Array
    (
        [site_id] => C
        [parent_id] => A
        [site_desc] => testtext
    )
    [3] => Array
    (
        [site_id] => D
        [parent_id] => B
        [site_desc] => testtext
    )
    [4] => Array
    (
        [site_id] => E
        [parent_id] => B
        [site_desc] => testtext
    )
    [5] => Array
    (
        [site_id] => F
        [parent_id] => B
        [site_desc] => testtext
    )
    [6] => Array
    (
        [site_id] => I
        [parent_id] => D
        [site_desc] => testtext
    )
    [7] => Array
    (
        [site_id] => J
        [parent_id] => D
        [site_desc] => testtext
    )
)
您可以使用此递归函数检索任何
站点id
(如果您知道id)的所有子/孙/曾孙/等:

function fetch_recursive($src_arr, $id, $parentfound = false, $cats = array())
{
    foreach($src_arr as $row)
    {
        if((!$parentfound && $row['site_id'] == $id) || $row['parent_id'] == $id)
        {
            $rowdata = array();
            foreach($row as $k => $v)
                $rowdata[$k] = $v;
            $cats[] = $rowdata;
            if($row['parent_id'] == $id)
                $cats = array_merge($cats, fetch_recursive($src_arr, $row['site_id'], true));
        }
    }
    return $cats;
}
例如,假设您想要检索
站点id
D
的所有子项,您可以使用如下函数:

$nodelist = fetch_recursive($pdostmt->fetchAll(PDO::FETCH_ASSOC), 'D');
print_r($nodelist);
将输出:

[0] => Array
(
    [site_id] => D
    [parent_id] => B
    [site_desc] => testtext
)
[1] => Array
(
    [site_id] => I
    [parent_id] => D
    [site_desc] => testtext
)
[2] => Array
(
    [site_id] => J
    [parent_id] => D
    [site_desc] => testtext
)

请注意,我们保留了父对象及其子对象、子对象等的信息(无论嵌套的深度有多深)。

您可以为此创建一个存储过程

下面是我在mysql中的实现

DROP PROCEDURE IF EXISTS SearchTree;
DELIMITER go

CREATE PROCEDURE SearchTree( IN root CHAR(1) )
BEGIN
  DECLARE rows SMALLINT DEFAULT 0;
  DROP TABLE IF EXISTS reached;
  CREATE TABLE reached (
    site_Id CHAR(1) PRIMARY KEY
  ) ENGINE=HEAP;
  INSERT INTO reached VALUES (root);
  SET rows = ROW_COUNT();
  WHILE rows > 0 DO
    INSERT IGNORE INTO reached 
      SELECT DISTINCT s.site_Id 
      FROM site AS s 
      INNER JOIN reached AS r ON s.parent_Id = r.site_Id;
    SET rows = ROW_COUNT();
    DELETE FROM reached 
      WHERE site_Id = root;
  END WHILE;
  SELECT * FROM reached;
  DROP TABLE reached;
END;
go
DELIMITER ;
CALL SearchTree('B');