显示每个父母的所有孩子

时间:2011-11-01 19:02:55

标签: php mysql html

我有一张桌子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结果集不是多维的。在for循环中放置SQL查询对性能来说太糟糕了。那你做什么?

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

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

4 个答案:

答案 0 :(得分:13)

有几种方法可以做到:

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
        }
    }
}

聚苯乙烯。如果您实际上并不想在表中显示 all 父项和子项,而只是表示属于单个系列的那些,那么您仍应尝试在SQL中进行过滤以避免获取太多记录。

答案 1 :(得分: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()结果数组,而不是将其重组为其他类似的东西,但是你会发现自己的东西不会比你已经存在的类似于固体记录集的数组表示更远有

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

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

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

答案 2 :(得分:1)

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

$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
  }
}

答案 3 :(得分:0)

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