文件系统中目录路径的分层/树数据库
Posted
技术标签:
【中文标题】文件系统中目录路径的分层/树数据库【英文标题】:Hierarchical/tree database for directories path in filesystem 【发布时间】:2011-10-11 18:22:37 【问题描述】:我想将目录(存在于磁盘上)存储到数据库中,维护它们的层次结构/树结构。
这是一张图:
(根) / \ 目录 2 目录 3 / \ \ 目录 4 目录 5 目录 6 / 目录7我正在使用 SQLite 数据库。
请给我建议:
将上述结构存储在 SQLite 数据库中的 SQL 查询,以及
当我选择一个查询时检索目录的完整路径。
即假设我选择Dir7
,那么我应该得到像ROOT/Dir2/Dir4/Dir7
这样的完整路径
【问题讨论】:
闭包表可能是一个不错的选择。看看What is the most efficient/elegant way to parse a flat table into a tree?,也看看Bill Karwin's幻灯片Models for hierarchical data 该主题再次出现在 SQLite mainling 列表中,Eduardo Morras(重新)指出了这个 SQLite vtable 扩展来处理 SQLite 存储库本身的层次结构:1)sqlite.org/src/artifact/…2)@987654325 @ 【参考方案1】:这是 SQLite 的快速关闭表示例。我没有包含将项目插入现有树的语句。相反,我刚刚手动创建了语句。您可以在Models for hierarchical data 幻灯片中找到插入和删除语句。
为了在插入目录 ID 时保持理智,我重命名了目录以匹配它们的 ID:
(ROOT)
/ \
Dir2 Dir3
/ \ \
Dir4 Dir5 Dir6
/
Dir7
创建表格
CREATE TABLE `filesystem` (
`id` INTEGER,
`dirname` TEXT,
PRIMARY KEY (`id`)
);
CREATE TABLE `tree_path` (
`ancestor` INTEGER,
`descendant` INTEGER,
PRIMARY KEY (`ancestor`, `descendant`)
);
将目录插入filesystem
表中
INSERT INTO filesystem (id, dirname) VALUES (1, 'ROOT');
INSERT INTO filesystem (id, dirname) VALUES (2, 'Dir2');
INSERT INTO filesystem (id, dirname) VALUES (3, 'Dir3');
INSERT INTO filesystem (id, dirname) VALUES (4, 'Dir4');
INSERT INTO filesystem (id, dirname) VALUES (5, 'Dir5');
INSERT INTO filesystem (id, dirname) VALUES (6, 'Dir6');
INSERT INTO filesystem (id, dirname) VALUES (7, 'Dir7');
创建闭包表路径
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 1);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 2);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 3);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 4);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 5);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 6);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 7);
INSERT INTO tree_path (ancestor, descendant) VALUES (2, 2);
INSERT INTO tree_path (ancestor, descendant) VALUES (2, 4);
INSERT INTO tree_path (ancestor, descendant) VALUES (2, 5);
INSERT INTO tree_path (ancestor, descendant) VALUES (2, 7);
INSERT INTO tree_path (ancestor, descendant) VALUES (3, 3);
INSERT INTO tree_path (ancestor, descendant) VALUES (3, 6);
INSERT INTO tree_path (ancestor, descendant) VALUES (4, 4);
INSERT INTO tree_path (ancestor, descendant) VALUES (4, 7);
INSERT INTO tree_path (ancestor, descendant) VALUES (5, 5);
INSERT INTO tree_path (ancestor, descendant) VALUES (6, 6);
INSERT INTO tree_path (ancestor, descendant) VALUES (7, 7);
运行一些查询
# (ROOT) and subdirectories
SELECT f.id, f.dirname FROM filesystem f
JOIN tree_path t
ON t.descendant = f.id
WHERE t.ancestor = 1;
+----+---------+
| id | dirname |
+----+---------+
| 1 | ROOT |
| 2 | Dir2 |
| 3 | Dir3 |
| 4 | Dir4 |
| 5 | Dir5 |
| 6 | Dir6 |
| 7 | Dir7 |
+----+---------+
# Dir3 and subdirectories
SELECT f.id, f.dirname
FROM filesystem f
JOIN tree_path t
ON t.descendant = f.id
WHERE t.ancestor = 3;
+----+---------+
| id | dirname |
+----+---------+
| 3 | Dir3 |
| 6 | Dir6 |
+----+---------+
# Dir5 and parent directories
SELECT f.id, f.dirname
FROM filesystem f
JOIN tree_path t
ON t.ancestor = f.id
WHERE t.descendant = 5;
+----+---------+
| id | dirname |
+----+---------+
| 1 | ROOT |
| 2 | Dir2 |
| 5 | Dir5 |
+----+---------+
# Dir7 and parent directories
SELECT f.id, f.dirname
FROM filesystem f
JOIN tree_path t
ON t.ancestor = f.id
WHERE t.descendant = 7;
+----+---------+
| id | dirname |
+----+---------+
| 1 | ROOT |
| 2 | Dir2 |
| 4 | Dir4 |
| 7 | Dir7 |
+----+---------+
SELECT f.id, f.dirname
FROM filesystem f
JOIN tree_path t
ON t.ancestor = f.id
WHERE t.descendant = (
SELECT id
FROM filesystem
WHERE dirname LIKE '%7%'
);
+----+---------+
| id | dirname |
+----+---------+
| 1 | ROOT |
| 2 | Dir2 |
| 4 | Dir4 |
| 7 | Dir7 |
+----+---------+
【讨论】:
@JOHN:对不起,我从来没有用过 orientDB,所以无法评论。 @JOHN:我不确定你的意思 - 请你举个例子吗? 我首先阅读了这些查询并一直想知道,“它是如何工作的?”我意识到您的tree_path
表是目录图的传递闭包 的邻接表表示,回答了以下问题:“这条路径是这条路径的可达后代吗?”和“这条路是这条路的可到达祖先吗?”不错。
@seh:是的,这是一个非常好的技术。虽然它比简单的邻接列表需要更多的条目,但选择数据的查询要简单得多。不过我不能把功劳。如果您想了解更多信息,请查看我在问题下方的 cmets 中发布的 Bill Karwin 链接。请注意,您还可以添加路径长度,这样可以更轻松地查找直接祖先/后代。
您的tree_path
表不应该包含“嵌套级别”或“深度”字段以使其成为闭合表吗?【参考方案2】:
我认为你应该阅读一个名为 Modified Preorder Tree Traversal 的方法:http://www.sitepoint.com/hierarchical-data-database/
该链接讨论了将分层数据存储到关系数据库中的两种方法:邻接表模型和改进的前序树遍历算法。
Modified Preorder Tree Traversal方法的主要思想是用指针注释所有节点以辅助导航和子树选择:
【讨论】:
【参考方案3】:您将分层数据表示为一系列节点,每个节点都有一个 ID 和一个父 ID。 您可以将您的存储在一个名为 DIRTAB 的表中,其中包含 2 个 ID 列和一个用于单个目录名称的文本:
ID -- as a primary key
PARENT_ID -- refers to the ID of the parent row in DIRTAB
DIRNAME -- the text of the name eg Dir5
SQLite 缺少 Oracle 必须处理分层数据的 CONNECT BY 子句,但我认为如果您准备接受一些丑陋的 SQL,您可以近似分层:
SELECT (CASE WHEN p5.DIRNAME IS NOT NULL THEN p5.DIRNAME || '/' ELSE '' END) ||
(CASE WHEN p4.DIRNAME IS NOT NULL THEN p4.DIRNAME || '/' ELSE '' END) ||
(CASE WHEN p3.DIRNAME IS NOT NULL THEN p3.DIRNAME || '/' ELSE '' END) ||
(CASE WHEN p2.DIRNAME IS NOT NULL THEN p2.DIRNAME || '/' ELSE '' END) ||
(CASE WHEN p1.DIRNAME IS NOT NULL THEN p1.DIRNAME || '/' ELSE '' END) ||
p0.DIRNAME as FULLPATH
FROM DIRTAB p0
LEFT OUTER JOIN DIRTAB p1 ON p1.ID = p0.PARENT_ID
LEFT OUTER JOIN DIRTAB p2 ON p2.ID = p1.PARENT_ID
LEFT OUTER JOIN DIRTAB p3 ON p3.ID = p2.PARENT_ID
LEFT OUTER JOIN DIRTAB p4 ON p4.ID = p3.PARENT_ID
LEFT OUTER JOIN DIRTAB p5 ON p5.ID = p4.PARENT_ID
WHERE p0.DIRNAME = 'Dir6'
这里的问题是您必须预测目录结构的最大深度并扩展 SQL 语句以应对。我已经完成了 6 个级别作为示例。 另外我假设 SQLite 连接空字符串没有问题。 (有些DB把它们当作null,把整个表达式结果都转换成null)
【讨论】:
以上是关于文件系统中目录路径的分层/树数据库的主要内容,如果未能解决你的问题,请参考以下文章