2017-08-16 2 views
3

Je travaille avec certaines tables qui représentent un système de fichiers, et j'ai besoin de sélectionner le chemin complet de chaque dossier comme une chaîne aplatie.SQL - Convertir la liste d'adjacence non nulle en chemin

Le premier tableau présente les détails de chaque dossier:

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

Le deuxième tableau énumère les fermetures transitifs des relations de dossier:

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) 

Pour les données d'échantillons, supposons que les dossiers suivants existent:

Documents/ 
Documents/Finance/ 
Documents/HumanResources/ 
Documents/HumanResources/Training/ 

Ceux-ci seraient conservés dans ces tableaux comme suit :

| 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 | 

Quelques détails à noter:

  1. Chaque dossier a une "ligne d'identité" dans FolderClosures, où AncestorFolderID = DescendantFolderID AND IsDirect = 0.

  2. Chaque dossier est pas un dossier de niveau supérieur a exactement une ligne dans FolderClosuresIsDirect = 1

  3. FolderClosures peut contenir plusieurs lignes par dossier, où AncestorFolderID <> DescendantFolderID AND IsDirect = 0. Chacun d'entre eux représente un «grand-parent» ou une relation plus éloignée.

  4. Étant donné qu'aucune colonne n'est nullable, aucune ligne n'indique explicitement qu'un dossier donné est un dossier de niveau supérieur. Cela peut seulement être discerné en vérifiant qu'il n'y a pas de lignes dans FolderClosuresIsDirect = 1 AND DescendantFolderID = SomeIDSomeID est l'ID du dossier en question.

Je veux être en mesure d'exécuter une requête qui renvoie ces données:

| FolderID | Path        | 
+----------+------------------------------------+ 
|  1 | Documents/       | 
|  2 | Documents/Finance/     | 
|  3 | Documents/HumanResources/   | 
|  4 | Documents/HumanResources/Training/ | 

dossiers peuvent être imbriqués à une profondeur illimitée, mais réaliste probablement jusqu'à 10 niveaux. Les requêtes peuvent nécessiter le renvoi de chemins pour quelques milliers de dossiers.

J'ai trouvé beaucoup de conseils sur la création de ce type de requête lorsque les données sont persistantes en tant que liste d'adjacence, mais je n'ai pas trouvé de réponse pour une configuration de fermeture transitive comme celle-ci. Les solutions de liste d'adjacence que j'ai trouvées s'appuient sur des lignes persistantes avec des ID de dossier parent nullable, mais cela ne fonctionne pas ici.

Comment puis-je obtenir la sortie désirée?

Si elle aide, j'utilise SQL Server 2016.

Répondre

2

Une façon d'obtenir la sortie désirée est de faire une requête récursive. Pour cela, je pense que le mieux est de n'utiliser que les lignes qui ont IsDirect = 1 et d'utiliser l'ancre comme tous les dossiers qui n'ont pas de parent direct dans FolderClosures, ce qui devrait être tous vos dossiers racine.

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 

Ce produit:

FolderID Path 
1   Documents 
2   Documents\Finance 
3   Documents\HumanResources 
4   Documents\HumanResources\Training 

Hope it helps.