如何高效将SQLite树形表加载为可便捷访问的树变量?
高效加载SQLite树状数据为可访问数组的方案
你的递归方法速度慢的核心原因是触发了数千次数据库查询——每处理一个节点就执行一次SQL,8000条数据会带来极大的数据库IO开销。最优方案是一次性拉取所有数据到内存,再在内存中构建树结构,只需要1次SQL查询,效率会大幅提升。
具体实现步骤
1. 一次性获取所有数据
先把整张表的数据一次性查出来,同时按PARENT和NAME排序,减少后续内存排序的工作:
// 只查询需要的字段(不要用SELECT *),进一步降低内存占用 $allData = Q("SELECT ID, PARENT, NAME /* 按需添加其他字段 */ FROM ORGCHART ORDER BY PARENT ASC, NAME ASC")->fetchAll();
2. 构建辅助映射数组
用两个数组做快速索引:
$nodeMap:以节点ID为键,存储完整节点数据,用于O(1)时间查找节点$childrenMap:以父节点ID为键,存储该父节点下所有子节点的ID列表
$nodeMap = []; $childrenMap = []; foreach ($allData as $row) { $id = $row['ID']; $parentId = $row['PARENT']; $nodeMap[$id] = $row; if (!isset($childrenMap[$parentId])) { $childrenMap[$parentId] = []; } $childrenMap[$parentId][] = $id; }
3. 构建树结构
可以选择递归或迭代方式,迭代方式更适合层级极深的树,避免栈溢出:
递归实现(代码简洁,适合层级不特别深的场景)
function buildTree($rootId, $nodeMap, $childrenMap) { $tree = []; // 遍历根节点下的所有子节点ID foreach ($childrenMap[$rootId] ?? [] as $childId) { $node = $nodeMap[$childId]; // 递归获取子节点的子树 $node['children'] = buildTree($childId, $nodeMap, $childrenMap); $tree[] = $node; } return $tree; } // 生成以PARENT=0为根的树结构 $allmap = buildTree(0, $nodeMap, $childrenMap);
迭代实现(避免栈溢出,适合大数据量+深层级场景)
function buildTreeIterative($rootId, $nodeMap, $childrenMap) { $tree = []; // 栈元素:[父节点ID, 父节点的子节点数组引用] $stack = [[$rootId, &$tree]]; while (!empty($stack)) { list($parentId, $parentChildrenRef) = array_pop($stack); // 倒序遍历子节点ID,保证最终顺序和递归一致 foreach (array_reverse($childrenMap[$parentId] ?? []) as $childId) { $childNode = $nodeMap[$childId]; $childNode['children'] = []; $parentChildrenRef[] = $childNode; // 将子节点压入栈,处理它的子节点 $stack[] = [$childId, &$parentChildrenRef[count($parentChildrenRef)-1]['children']]; } } return $tree; } $allmap = buildTreeIterative(0, $nodeMap, $childrenMap);
效率优势
- 仅1次SQL查询,彻底消除了多次数据库请求的IO开销
- 内存操作的时间复杂度为O(n),远低于原递归查询的O(n²)
- 提前在SQL中完成排序,比内存排序更高效
内容的提问来源于stack exchange,提问作者Michael Chourdakis
相关产品推荐
相关产品推荐

