2014-03-24 133 views
0

我有具有以下結構/數據的數據庫中的表:分層數據到陣列

n_id  n_parent_id  ... some other fields ... 
====  ===========  ========================= 
1   null   ... 
2   null   ... 
... 
11   1   ... 
12   1   ... 
... 
25   2   ... 
... 
65   11   ... 
66   11   ... 
... 

此表存儲分層數據,如可以從樣品上面可以看到。我需要這個加載到樹狀FASION一個PHP陣列,使陣列將包含這樣的事情:

Array 
(
    [1] => Array 
     (
      [n_id] => 1 
      [n_parent_id] => 
      [other_data] => ... 
      [children] => Array 
       (
        [11] => Array 
         (
          [n_id] => 11 
          [n_parent_id] => 1 
          [other_data] => ... 
          [children] => Array 
           (
            [65] => Array 
             (
              [n_id] => 65 
              [n_parent_id] => 11 
              [other_data] => ... 
             ) 
           ) 
    ... and so on ... 
) 

我可以輕鬆應對一層:

//ordering will ensure that parent row is always read before children rows 
//my data is set up in this way. 
$query = "select n_id, n_parent_id, other_data from hierarchy_table order by n_parent_id, n_id"; 
if(($dbs = $dbh->query($query)) === FALSE) { 
    $e = $dbh->errorInfo(); 
    // ... deal with error 
} 
$result = array(); 
while($row = $dbs->fetch(PDO::FETCH_ASSOC)) { 
    if(is_null($row['n_parent_id'])) { 
     $result[$row['n_id']] = array(
      'n_id' => $row['n_id'], 
      'n_parent_id' => null, 
      'other_data' => ..., 
      'children' => array() 
     ); 
    } 
    elseif(isset($result[$row['n_parent_id']])) { 
     $result[$row['n_parent_id']]['children'][$row['n_id']] = array(
      'n_id' => $row['n_id'], 
      'n_parent_id' => $row['n_parent_id'], 
      'other_data' => ... 
      children => array() 
     ); 
    } 
} 

不過我似乎無法讓我的頭擴展到多個級別,而不必每次需要添加行時都必須遞歸遍歷整個數組。當然,如果它是Java或C,我只會存儲指向數據結構的指針,這將解決問題,但在PHP中,這並不那麼容易。在這一切的結尾,我需要將json_encode這個發送給客戶端。

This question涵蓋了類似的問題,但我沒有在數據庫中的實際分層信息 - 只有父母身份證。

對此的任何幫助表示讚賞。

編輯:我的數據庫表包含數十萬行,因此性能很重要。

+0

[遞歸函數以生成從數據庫結果多維數組](HTTP的可能重複://堆棧溢出。com/questions/8587341/recursive-function-to-generate-multidimensional-array-from-database-result) – deceze

+0

@bpositive不要做無意義的/無用的編輯 - 它們沒有幫助,將被還原。 –

+0

@deceze感謝您的指針。鏈接的問題確實提供了一種解決方案,但在我的情況下,數據庫表可能包含數十萬條記錄,因此,您在該答案中提供的功能將非常非常低效......自然地,掃描/循環播放整個多次數組是一個選項,但如果可能的話,我想避免它。 –

回答

1

經過一番苦苦掙扎之後,我設法通過一次遍歷記錄集(只讀取一次記錄)獲得我需要的東西 - 使用引用。由於內存引用支持在PHP中相當有限,因此需要一些有趣的東西來保持工作正常(例如,我正在從數據庫中讀取每一行的新變量名稱)。無論如何,這裏就是我結束了與代碼(此代碼只能與idparent_id交易 - 但它是微不足道的讀取/存儲更多的數據):

$dbh = new PDO(CONNECT_STRING, USERNAME, PASSWORD); 
$dbs = $dbh->query("SELECT n_id, n_parent_id from test_table order by n_parent_id, n_id"); 
$elems = array(); 

while(($row = $dbs->fetch(PDO::FETCH_ASSOC)) !== FALSE) { 
    $row['children'] = array(); 
    $vn = "row" . $row['n_id']; 
    ${$vn} = $row; 
    if(!is_null($row['n_parent_id'])) { 
     $vp = "parent" . $row['n_parent_id']; 
     if(isset($data[$row['n_parent_id']])) { 
      ${$vp} = $data[$row['n_parent_id']]; 
     } 
     else { 
      ${$vp} = array('n_id' => $row['n_parent_id'], 'n_parent_id' => null, 'children' => array()); 
      $data[$row['n_parent_id']] = &${$vp}; 
     } 
     ${$vp}['children'][] = &${$vn}; 
     $data[$row['n_parent_id']] = ${$vp}; 
    } 
    $data[$row['n_id']] = &${$vn}; 
} 
$dbs->closeCursor(); 

$result = array_filter($data, function($elem) { return is_null($elem['n_parent_id']); }); 
print_r($result); 

當這個數據執行:

mysql> select * from test_table; 
+------+-------------+ 
| n_id | n_parent_id | 
+------+-------------+ 
| 1 |  NULL | 
| 2 |  NULL | 
| 3 |   1 | 
| 4 |   1 | 
| 5 |   2 | 
| 6 |   2 | 
| 7 |   5 | 
| 8 |   5 | 
+------+-------------+ 

最後print_r產生這樣的輸出:

Array 
(
    [1] => Array 
     (
      [n_id] => 1 
      [n_parent_id] => 
      [children] => Array 
       (
        [3] => Array 
         (
          [n_id] => 3 
          [n_parent_id] => 1 
          [children] => Array 
           (
           ) 

         ) 

        [4] => Array 
         (
          [n_id] => 4 
          [n_parent_id] => 1 
          [children] => Array 
           (
           ) 

         ) 

       ) 

     ) 

    [2] => Array 
     (
      [n_id] => 2 
      [n_parent_id] => 
      [children] => Array 
       (
        [5] => Array 
         (
          [n_id] => 5 
          [n_parent_id] => 2 
          [children] => Array 
           (
            [7] => Array 
             (
              [n_id] => 7 
              [n_parent_id] => 5 
              [children] => Array 
               (
               ) 

             ) 

            [8] => Array 
             (
              [n_id] => 8 
              [n_parent_id] => 5 
              [children] => Array 
               (
               ) 

             ) 

           ) 

         ) 

        [6] => Array 
         (
          [n_id] => 6 
          [n_parent_id] => 2 
          [children] => Array 
           (
           ) 

         ) 

       ) 

     ) 

) 

這正是我一直在尋找。

1

由於我還面臨幾乎相同的問題,Aleks G的創意(!)解決方案並未完全滿足我的需求,因爲我使用嵌套集模型保存了分層數據,這是我的解決方案嵌套集(需要一段時間才能實現)。必須基於前序遍歷來對$data數組進行排序。

使用例:

$data = 
[ 
    0 => ['ID' => 0, 'depth' => 0], 
    1 => ['ID' => 1, 'depth' => 1], 
    2 => ['ID' => 2, 'depth' => 2], 
    3 => ['ID' => 6, 'depth' => 2], 
    4 => ['ID' => 10, 'depth' => 1] 
]; 

$IDs = hierachicDataToArray($data); 
print_r($IDs); 

$IDs = hierachicDataToArray($data, true); 
print_r($IDs); 

輸出:

Array 
(
    [0] => Array 
     (
      [1] => Array 
       (
        [2] => 2 
        [6] => 6 
       ) 

      [10] => 10 
     ) 

) 

Array 
(
    [0] => Array 
     (
      [ID] => 0 
      [depth] => 0 
      [children] => Array 
       (
        [1] => Array 
         (
          [ID] => 1 
          [depth] => 1 
          [children] => Array 
           (
            [2] => Array 
             (
              [ID] => 2 
              [depth] => 2 
              [children] => Array 
               (
               ) 

             ) 

            [6] => Array 
             (
              [ID] => 6 
              [depth] => 2 
              [children] => Array 
               (
               ) 

             ) 

           ) 

         ) 

        [10] => Array 
         (
          [ID] => 10 
          [depth] => 1 
          [children] => Array 
           (
           ) 

         ) 

       ) 

     ) 

) 

方法:

/** 
* Convert hierarchic data records to a multidimensional array. 
* Expects an array in the form: [<i> => ['ID' => <int ID>, 'depth' => <int depth>, '<string>' => <mixed>, ...]] 
* At least the 'ID' and 'depth' key/value pairs must exist. 
* @author: lsblsb[at]gmx.de 
* @copyright: GPL-3.0 
* 
* @param array $data The data array. 
* @param bool $incData = false Whether to include additional data or not. 
* @param bool $IDKeys = true Whether to use IDs as key or not (false only possible when $incData = true) 
* 
* @return array[] 
*/ 
function hierarchicDataToArray(array $data, $incData = false, $IDKeys = true) 
{ 
    $nodes = []; 

    foreach($data as $i => $record) 
    { 
     $ID = $record['ID']; 
     $depth = $record['depth']; 
     $prevRecord = isset($data[$i-1]) ? $data[$i-1] : false; 
     $prevDepth = $prevRecord ? $prevRecord['depth'] : false; 
     $prevID = $prevRecord ? $prevRecord['ID'] : false; 
     $nextRecord = isset($data[$i+1]) ? $data[$i+1] : false; 
     $nextDepth = $nextRecord ? $nextRecord['depth'] : false; 
     $nextID = $nextRecord ? $nextRecord['ID'] : false; 

     if($prevRecord && $prevDepth >= $depth) 
     { 
      $pID = $depthIDs[$depth-1]; 
      if($depth == 1) 
      { 
       if($incData) 
        $nodes[$pID]['children'][$ID] = &$refs[$ID]; 
       else 
        $nodes[$pID][$ID] = &$refs[$ID]; 
      } 
      else 
      { 
       if($incData) 
        $refs[$pID]['children'][$ID] = &$refs[$ID]; 
       else 
        $refs[$pID][$ID] = &$refs[$ID]; 
      } 
     } 

     if($nextRecord && $nextDepth > $depth) 
     { 
      if($depth == 0) 
      { 
       if($incData) 
       { 
        if(!isset($nodes[$ID])) $nodes[$ID] = $record; 
        $nodes[$ID]['children'][$nextID] = &$refs[$nextID]; 
       } 
       else 
        $nodes[$ID][$nextID] = &$refs[$nextID]; 
      } 
      else 
      { 
       if($incData) 
       { 
        if(!isset($refs[$ID])) $refs[$ID] = $record; 
        $refs[$ID]['children'][$nextID] = &$refs[$nextID]; 
       } 
       else 
        $refs[$ID][$nextID] = &$refs[$nextID]; 
      } 
     } 
     else 
     { 
      $node = $incData ? $record + ['children' => []] : $ID; 
      $refs[$ID] = $node; 
     } 

     if(!$IDKeys && $incData) 
     { 
      if(!$nextRecord) 
      { 
       $nodes = array_values($nodes); 
       $nodes[0]['children'] = array_values($nodes[0]['children']); 
      } 
      elseif($nextDepth < $depth) 
      { 
       $pID = $depthIDs[$depth-1]; 
       $refs[$pID]['children'] = array_values($refs[$pID]['children']); 
      } 
     } 

     $depthIDs[$depth] = $ID; 
    } 

    return $nodes; 
}