我在数据库中有一个表,其中包含以下结构/数据:
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涵盖了类似的问题,但我在数据库中没有实际的分层信息 - 只有父ID。
对此有任何帮助表示赞赏。
编辑:我的数据库表包含数十万行,因此性能非常重要。
答案 0 :(得分:1)
经过一番挣扎之后,我设法通过记录集上的一次传递(只读取每个记录一次)得到我需要的东西 - 使用引用。由于内存引用支持在PHP中相当有限,因此需要保留一些有趣的东西(例如,我从数据库中读取的每一行的新变量名称)。无论如何,这是我最终得到的代码(此代码仅处理id
和parent_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
(
)
)
)
)
)
这正是我所寻找的。 p>
答案 1 :(得分: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;
}