【问题标题】:Showing all of the children of each parent显示每个父母的所有孩子
【发布时间】:2011-12-19 18:24:13
【问题描述】:

我有一张桌子People。我想显示一个由每个父母组成的 HTML 表格,他们的所有孩子都在他们的正下方。

 _________
|People   |_____________________________________________
|-------------------------------------------------------|
| id     | parent | firstname     | lastname            |
|-------------------------------------------------------|
| 1        0        James           Donovan             |
| 2        0        Jeffrey         Williams            |
| 3        0        Emmit           Herring             |
| 4        2        Carol           Williams            |
| 5        2        Sarah           Williams            |
| 6        1        Nikolai         Donovan             | 
|_______________________________________________________|

预期输出:

 ________________________________________________
|Jeffrey Williams                                |
|------------------------------------------------|
|  - Carol Williams                              |
|  - Sarah Williams                              |
|________________________________________________|
|James Donovan                                   |
|------------------------------------------------|
|  - Nikolai Donovan                             |
|________________________________________________|
|Emmit Herring                                   |
|------------------------------------------------|
|________________________________________________|

如何构建一个关联数组,其中包含要迭代的正确结果集?我对构建最终数组的正确 SQL 和正确 PHP 感到困惑。

具体来说,我不确定如何显示两个 MySQL 表之间的层次关系。据我所知,SQL 结果集不是多维的。将 SQL 查询放在 for 循环中对性能来说很糟糕。那你怎么办?

我想我正在寻找 MySQL 中的邻接列表实现。

如果我可以将所有内容分成两张表,这个问题应该很容易,但不幸的是我必须坚持这种不正常的表结构。

【问题讨论】:

  • 为什么我必须使用 jQuery 来显示像每个父母的孩子一样简单的东西?这里没有PHP解决方案吗?
  • @Mark 我想我的问题还不够清楚。我不是来帮忙做作业的。我更新了问题。
  • 这是一个 2 级树吗?或者你允许孙子等?
  • 这是一个 2 级树。没有孙子。
  • 当您有 2 个父母时,您是希望将它们彼此内联显示,还是作为单独的实体显示,具有相同的孩子?

标签: php mysql html


【解决方案1】:

有几种方法可以做到:

1. 显而易见的是首先获取所有父母的列表,然后在循环中为每个父母的孩子运行单独的查询。你说这“对性能来说很糟糕”,但实际上不应该这样,假设你在 parent 列上有一个索引并且你的 MySQL 服务器不在地球的另一端。


2.如果您真的想在单个查询中执行此操作,您可以在表上使用LEFT JOIN 来对抗自身:

SELECT
  p.id AS parent_id,
  p.firstname AS parent_firstname,
  p.lastname  AS parent_lastname,
  c.id AS child_id,
  c.firstname AS child_firstname,
  c.lastname  AS child_lastname
FROM
  People AS p
  LEFT JOIN People AS c ON c.parent = p.id
WHERE p.parent = 0
ORDER BY p.id

同样,您真的,真的需要在 parent 列上建立索引。 ORDER BY 子句用于确保将每个父级的子级排序在一起;你可以改变它,例如如果您希望名称按字母顺序排序,则可以使用 p.lastname, p.firstname, p.id, c.lastname, c.firstname, c.id 之类的东西。在 PHP 中,您需要遍历结果并在父 ID 更改时打印一个新标题(并记住处理 child_* 列为 NULL 的情况),例如:

$res = mysql_query( $sql );
$last_parent_id = 0;
while ( $row = mysql_fetch_object( $res ) ) {
    if ( $row->parent_id != $last_parent_id ) {
        // print parent header
        $last_parent_id = $row->parent_id;
    }
    if ( $row->child_id ) {
        // print child row
    }
}

3. 第三个选项是通过简单的SELECT * FROM People 查询获取所有行并在 PHP 中构建树:

$res = mysql_query( "SELECT * FROM People" );  // add WHERE clauses if needed
$names = array();
$parents = array();
$children = array();

while ( $row = mysql_fetch_object( $res ) ) {
    $names[ $row->id ] = array( $row->firstname, $row->lastname );
    if ( $row->parent == 0 ) {
        $parents[] = $row->id;
    } else {
        if ( !array_key_exists( $row->parent, $children ) )
            $children[ $row->parent ] = array();
        $children[ $row->parent ][] = $row->id;
    }
}

foreach ( $parents as $parent_id ) {
    // print parent header
    if ( array_key_exists( $parent_id, $children ) ) {
        foreach ( $children[ $parent_id ] as $child_id ) {
            // print child row
        }
    }
}

附言。如果您实际上不想显示所有表中的父母和孩子,而只是说属于单个家庭的人,那么您仍然应该尝试在 SQL 中进行过滤以避免获取的记录太多。

【讨论】:

    【解决方案2】:

    根据传统方法,我认为从 SQL 开始,连接表(即使在这种情况下左表和右表相同),可能是一个很好的起点。

    这主要是因为使用 RDBMS 时,您必须始终处理表格结构,并以保证数据一致性的方式连接表。

    所以,从以下内容开始:

    SELECT 
           a.id parent_id, a.firstname parent_name, a.lastname parent_lastname, 
           b.id child_id, b.firstname child_firstname, b.lastname child_lastname
    FROM 
           People a LEFT OUTER JOIN People b ON a.id = b.parent
    WHERE  
           a.parent = 0;
    

    其次,您应该更喜欢使用“fetch_all”策略(例如,使用mysqli php 扩展名,但也可以使用PDO),这将使您能够通过一个操作获得整个在二维关联数组中获取的结果集。

    此时您可以选择您的路径。

    All-PHP:您可以使用 PHP 遍历数组并直接构建表示标记以显示根据需要组织的数据,echo将 html 字符串指向浏览器。

    AJAX:例如,如果您的 PHP 脚本是通过 AJAX 调用的,您也可以遍历查询结果数组,但这次将其解释为构建一个 JSON 结构,您可以使用它来响应调用,就像那:

    {
        "1": {
            "id": 1,
            "firstname": "James",
            "lastname": "Donovan",
            "children": {
                "6": {
                    "id": 6,
                    "firstname": "Nikolai",
                    "lastname": "Donovan"   
                }
            }
        },
        "2": {
            "id": 2,
            "firstname": "Jeffrey",
            "lastname": "Williams",
            "children": {
                "4": {
                    "id": 4,
                    "firstname": "Carol",
                    "lastname": "Williams"  
                },
                "5": {
                    "id": 5,
                    "firstname": "Sarah",
                    "lastname": "Williams"  
                }
            }
        },
        "3": {
            "id": 3,
            "firstname": "Emmit",
            "lastname": "Herring",
            "children": { }
        }
    }
    

    这样的表示对于数据交换会更好,因为您的客户端 JavaScript 可以无缝地识别它并遍历它以填充预先存在的空表骨架。当然,您可以让 PHP 直接使用json_encode() 结果数组,而不是将其重组为类似的其他东西,但是您会发现自己的东西不会比您已经拥有的可靠的类似记录集的数组表示走得更远。

    最后,全mysql解决方案是准备一个存储过程,有目的地构造您正在寻找的数据结构,例如每个家庭 1 行,第一列是父母的全名,后面的列是孩子的全名(如果该人没有孩子,则为空字段,例如 Emmit Herring)。

    您可以再次使用 PHP “fetch_all”结果集,遍历数组,然后就完成了。

    因此,如果性能是一个问题,那么最后一种方法应该可以保证您获得最佳结果,即使必须说服务器在计算负载和内存占用方面付出了代价,如果您要处理拥有大量数据。

    【讨论】:

      【解决方案3】:

      你可以在循环中使用循环:

      $res = mysql_query("SELECT PARENT");
      while( $row = mysql_fetch_assoc($res) )
      {
      
        // echo parent
      
        $res2 = mysql_query("SELECT CHILD WHERE PARENT SOMETHING");
        while( $row2 = mysql_fetch_assoc($res2) )
        {
      
          // echo child
        }
      }
      

      或者,保留它以备后用,并存储一个标志。

      $people = array();
      
      $res = mysql_query("SELECT PARENT");
      while( $row = mysql_fetch_assoc($res) )
      {
        $people[] = array('is_parent' => true,
                          'info'      => $row);
      
        $res2 = mysql_query("SELECT CHILD WHERE PARENT SOMETHING");
        while( $row2 = mysql_fetch_assoc($res2) )
        {    
          $people[] = array('is_parent' => false,
                            'info'      => $row2);
        }
      }
      
      // later
      
      foreach( $people as $person )
      {
        if( $person['is_parent'] )
        {
          // echo parent
        }
        else
        {
          // echo child
        }
      }
      

      【讨论】:

      • 当然可以,但正如我在问题中提到的那样,随着表的增长,这样做会导致无法接受的性能问题。
      【解决方案4】:

      为什么不在 JavaScript 中创建一个多线性数组呢?之后,只需循环遍历数组即可在 DOM 中获取结果。

      【讨论】:

      • 因为不应要求网站用户启用 Javascript。为什么不使用 Javascript 操作 DOM 就不能显示 Parent -> Children 关系列表?我正在使用 PHP 和 MySQL。
      猜你喜欢
      • 2021-07-04
      • 2022-01-15
      • 1970-01-01
      • 2014-10-23
      • 1970-01-01
      • 2015-03-26
      • 1970-01-01
      • 1970-01-01
      • 2018-01-29
      相关资源
      最近更新 更多