2017-08-16 50 views
3

我有一些表格,代表了文件系统的工作,我需要选择每个文件夹的完整路径作为扁平化线。SQL - 转换非空邻接表到路径

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

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 | 

一些细节需要注意:

  1. 每个文件夹中含有FolderClosures,其中AncestorFolderID = DescendantFolderID AND IsDirect = 0 “身份排”。

  2. 不是一个顶层文件夹中的每个文件在FolderClosures只有一行,其中IsDirect = 1

  3. FolderClosures可以包含每个文件夹多行,其中AncestorFolderID <> DescendantFolderID AND IsDirect = 0。这些都代表“祖父母”或更远的关系。

  4. 由于没有列是空的,没有行明确规定一个给定的文件夹是一个顶层文件夹。这只能通过检查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

回答

2

的一种方式,从而获得所需的输出是做一个递归查询。为此,我认为最好的做法是只使用IsDirect = 1的行,并将锚点作为所有根文件夹,作为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 

希望它能帮助。