【问题标题】:Hierarchical/tree database for directories path in filesystem文件系统中目录路径的分层/树数据库
【发布时间】:2011-10-11 18:22:37
【问题描述】:

我想将目录(存在于磁盘上)存储到数据库中,维护它们的层次结构/树结构。

这是一张图:

(根) / \ 目录 2 目录 3 / \ \ 目录 4 目录 5 目录 6 / 目录7

我正在使用 SQLite 数据库。

请给我建议:

  1. 将上述结构存储在 SQLite 数据库中的 SQL 查询,以及

  2. 当我选择一个查询时检索目录的完整路径。

    即假设我选择Dir7,那么我应该得到像ROOT/Dir2/Dir4/Dir7这样的完整路径

【问题讨论】:

标签: sql sqlite path tree hierarchical-data


【解决方案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)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-19
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      相关资源
      最近更新 更多