Php 将分层数据从数据库加载到数组中
我在数据库中有一个具有以下结构/数据的表:Php 将分层数据从数据库加载到数组中,php,arrays,database,hierarchical-data,Php,Arrays,Database,Hierarchical Data,我在数据库中有一个具有以下结构/数据的表: n_id n_parent_id ... some other fields ... ==== =========== ========================= 1 null ... 2 null ... ... 11 1 ... 12 1
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\u encode
发送到客户端
介绍了一个类似的问题,但我在数据库中没有实际的层次结构信息-只有父id
在此方面的任何帮助都将不胜感激
编辑:我的数据库表包含数十万行,因此性能很重要。经过一番努力,我通过使用引用对记录集进行一次遍历(仅读取每条记录一次)来获得所需的内容。由于PHP中对内存引用的支持相当有限,因此需要一些有趣的东西来保持thin正常工作(例如,我从DB中读取的每一行都有一个新的变量名)。不管怎样,下面是我最后得到的代码(此代码只处理
id
和parent\u id
——但读取/存储更多数据很简单):
对该数据执行时:
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
(
)
)
)
)
)
这正是我所寻找的。因为我也面临着几乎相同的问题,而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;
}
/**
*将层次数据记录转换为多维数组。
*需要以下格式的数组:[=>['ID'=>,'depth'=>,''=>,…]]
*至少必须存在“ID”和“深度”键/值对。
*@author:lsblsb[at]gmx.de
*@版权所有:GPL-3.0
*
*@param array$data数据数组。
*@param bool$incData=false是否包含其他数据。
*@param bool$IDKeys=true是否将ID用作密钥(仅当$incData=true时可能为false)
*
*@return数组[]
*/
函数层次结构CDATA阵列(数组$data、$incData=false、$IDKeys=true)
{
$nodes=[];
foreach($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;
如果($prevRecord&&$prevDepth>=$depth)
{
$pID=$depthIDs[$depth-1];
如果($depth==1)
{
如果($incData)
$nodes[$pID]['children'][$ID]=&$refs[$ID];
其他的
$nodes[$pID][$ID]=&$refs[$ID];
}
其他的
{
如果($incData)
$refs[$pID]['children'][$ID]=&$refs[$ID];
其他的
$refs[$pID][$ID]=&$refs[$ID];
}
}
如果($nextRecord&&$nextDepth>$depth)
{
如果($depth==0)
{
如果($incData)
{
如果(!isset($nodes[$ID]))$nodes[$ID]=$record;
$nodes[$ID]['children'][$nextID]=&$refs[$nextID];
}
其他的
$nodes[$ID][$nextID]=&$refs[$nextID];
}
其他的
{
如果($incData)
{
如果(!isset($refs[$ID]))$refs[$ID]=$record;
$refs[$ID]['children'][$nextID]=&$refs[$nextID];
}
其他的
$refs[$ID][$nextID]=&$refs[$nextID];
}
}
其他的
{
$node=$incData?$record+['children'=>[]]:$ID;
$refs[$ID]=$node;
}
如果(!$IDKeys&&$incData)
{
如果(!$nextRecord)
{
$nodes=数组_值($nodes);
$nodes[0]['children']=数组_值($nodes[0]['children']);
}
elseif($nextDepth<$depth)
{
$pID=$depthIDs[$depth-1];
$refs[$pID]['children']=数组_值($refs[$pID]['children']);
}
}
$depthIDs[$depth]=$ID;
}
返回$nodes;
}
@bpositive的可能副本请勿进行无意义/无用的编辑-这些编辑没有帮助,将被还原。@deceze感谢您的指针。链接问题确实提供了一种解决方案,但是在我的例子中,数据库表可能包含数十万条记录,因此您在该答案中提供的功能将非常低效。。。当然,在整个阵列上多次扫描/循环是一种选择,但如果可能的话,我想避免。这很公平,但这会使问题变得更难(正如你已经知道的)。@deceze是的,我知道。。。PHP不是速度优化的最佳语言。。。
/**
* 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;
}