SQL - 将非空邻接列表转换为路径

标签 sql sql-server path filesystems transitive-closure

我正在处理一些代表文件系统的表,我需要选择每个文件夹的完整路径作为扁平字符串。

第一个表列出了每个文件夹的详细信息:

CREATE TABLE Folders(
    FolderID int IDENTITY(1,1) NOT NULL,
    [Name] nvarchar(255) NOT NULL)

第二个表列出了文件夹关系的传递闭包:
CREATE TABLE FolderClosures(
    FolderClosuresID int IDENTITY(1,1) NOT NULL,
    AncestorFolderID int NOT NULL, --Foreign key to Folders.FolderID
    DescendantFolderID int NOT NULL --Foreign key to Folders.FolderID
    IsDirect bit NOT NULL)

对于示例数据,假设存在以下文件夹:
Documents/
Documents/Finance/
Documents/HumanResources/
Documents/HumanResources/Training/

这些将保留在这些表中,如下所示:
| FolderID | Name           |
+----------+----------------+
|        1 | Documents      |
|        2 | Finance        |
|        3 | HumanResources |
|        4 | Training       |

| FolderClosureID | AncestorFolderID | DescendantFolderID | IsDirect |
+-----------------+------------------+--------------------+----------+
|               1 |                1 |                  1 |        0 |
|               2 |                2 |                  2 |        0 |
|               3 |                1 |                  2 |        1 |
|               4 |                3 |                  3 |        0 |
|               5 |                1 |                  3 |        1 |
|               6 |                4 |                  4 |        0 |
|               7 |                1 |                  4 |        0 |
|               8 |                3 |                  4 |        1 |

一些需要注意的细节:
  • 每个文件夹在 FolderClosures 中都有一个“身份行” ,其中 AncestorFolderID = DescendantFolderID AND IsDirect = 0 .
  • 每个不是顶级文件夹的文件夹在 FolderClosures 中都只有一行哪里IsDirect = 1
  • FolderClosures每个文件夹可以包含多行,其中 AncestorFolderID <> DescendantFolderID AND IsDirect = 0 .每一个都代表一个“祖 parent ”或更远的关系。
  • 由于没有列可以为空,因此没有行明确声明给定文件夹是顶级文件夹。这只能通过检查 FolderClosures 中没有行来辨别。哪里IsDirect = 1 AND DescendantFolderID = SomeID哪里SomeID是相关文件夹的 ID。

  • 我希望能够运行返回此数据的查询:
    | FolderID | Path                               |
    +----------+------------------------------------+
    |        1 | Documents/                         |
    |        2 | Documents/Finance/                 |
    |        3 | Documents/HumanResources/          |
    |        4 | Documents/HumanResources/Training/ |
    

    文件夹的嵌套深度可能不受限制,但实际上可能最多只能嵌套 10 层。查询可能需要返回几千个文件夹的路径。

    当数据作为邻接列表持久化时,我发现了很多关于创建此类查询的建议,但我一直无法找到像这样的传递闭包设置的答案。我发现的邻接列表解决方案依赖于使用可为空的父文件夹 ID 持久化的行,但这在这里不起作用。

    如何获得所需的输出?

    如果有帮助,我正在使用 SQL Server 2016。

    最佳答案

    获得所需输出的一种方法是进行递归查询。为此,我认为最好只使用具有 IsDirect = 1 的行。并将 anchor 用作 FolderClosures 中没有直接父级的所有文件夹,这应该是您所有的根文件夹。

    WITH FoldersCTE AS (
        SELECT  F.FolderID, CAST(F.Name as NVARCHAR(max)) Path
        FROM    Folders F
        WHERE   NOT EXISTS (
            SELECT 1 FROM FolderClosures FC WHERE FC.IsDirect = 1 AND FC.DescendantFolderID = F.FolderID
        )
        UNION ALL
        SELECT  F.FolderID, CONCAT(PF.Path, '\', F.Name)
        FROM    FoldersCTE PF
                INNER JOIN FolderClosures FC
                    ON  FC.AncestorFolderID = PF.FolderId
                    AND FC.IsDirect = 1
                INNER JOIN Folders F
                    ON F.FolderID = FC.DescendantFolderID
    )
    SELECT * 
    FROM    FoldersCTE  
    OPTION (MAXRECURSION 1000) --> how many nested levels you think you will have
    

    这产生:
    FolderID    Path
    1           Documents
    2           Documents\Finance
    3           Documents\HumanResources
    4           Documents\HumanResources\Training
    

    希望能帮助到你。

    关于SQL - 将非空邻接列表转换为路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45717930/

    相关文章:

    mysql - 在 Access VBA 中循环 Access strSQL 中的日期

    mysql - 如何将表格从 1 行多列重组为多行少列?

    sql - 执行子查询时表别名重新标记

    SQL Server 2012 结果网格

    jquery - 我需要定义 <?php bloginfo ('template_directory' ); ?> 在我的 jQuery 脚本中。如何?

    mysql - 错误代码 1215 : Cannot add foreign key constraint

    sql-server - Sql Server 日期格式 DD-Mon-YY

    php - 如何使用内容类型 :image/jpeg properly?

    linux - CMake find_path 适用于 Linux 但不适用于 MingW。我该如何解决?

    python - Windows 上 python 的长路径 - os.stat() 相对路径失败?