2017-01-24 3 views
3

Ich habe eine Tabelle, die Ordner Pfade enthält. Ich muss alle "Lücken" zwischen diesen Ordnern in der Hierarchie finden. meine ich, dass, wenn die Tabelle diese drei Ordner enthält:SQL: Finden Sie fehlende Hierarchie Ordner (Pfade) in einer Tabelle

'A' 
'A\B\C' 
'A\B\C\D\E\F\G' 

Ich brauche die folgenden fehlende Ordner in der Hierarchie zu finden:

'A\B' 
'A\B\C\D' 
'A\B\C\D\E' 
'A\B\C\D\E\F' 

Diese Tabelle mehr als 250.000 Datensätze des Ordners enthält , so suchen wir für die meisten effiziente Weg, dies zu tun, sonst wird das Skript für lange Zeit stecken bleiben, Zeit, die wir nicht haben.

Kommentar: Ich habe keine Liste aller Ordner. Was ich habe, sind die "root" -Ordner und die "leafs" -Ordner, die ich brauche, um die "Lücken" zwischen ihnen in der Hierarchie zu finden.

Zweiter Kommentar: Die Tabelle kann enthält mehr als eine Hierarchie und wir brauchen die „Lücken“ in alle der Hierarchien zu finden. Zu diesem Thema gibt es 2 weitere int-Spalten: "DirID" und "BaseDirID". Die Spalte "DirID" ist die ID-Spalte in unserer Tabelle. Die "BaseDirID" enthält die ID des ersten Ordners in der Hierarchie. Daher teilen sich alle Ordner (Pfade) derselben Hierarchie denselben Wert in dieser Spalte. Beispieldaten zum Beispiel:

Example sample data

DirID BaseDirID DisplayPath 
1 1 'A' 
2 1 'A\B\C' 
3 1 'A\B\C\D\E' 
4 4 'U' 
5 4 'U\V\W' 
6 4 'U\V\W\X\Y' 

Also brauchen wir folgende Daten zu finden:

Expected Results

BaseDirID DisplayPath 
1 'A\B' 
1 'A\B\C\D' 
4 'U\V' 
4 'U\V\W\X' 

Vielen Dank im Voraus.

+0

Dies ist etwas, was ich außerhalb von SQL tun würde. Jede Programmiersprache wird in diesem Fall wahrscheinlich besser funktionieren als t-sql. –

Antwort

2

Hier ist ein Ansatz Recursive CTE und Split-String-Funktion

;WITH existing_hierachies 
    AS (SELECT DirID, 
       BaseDirID, 
       DisplayPath 
     FROM (VALUES (1,1,'A'), 
         (2,1,'A\B\C'), 
         (3,1,'A\B\C\D\E'), 
         (4,4,'U'), 
         (5,4,'U\V\W'), 
         (6,4,'U\V\W\X\Y')) tc (DirID, BaseDirID, DisplayPath)), 
    folders_list 
    AS (SELECT ItemNumber, 
       item fol, 
       BaseDirID 
     FROM (SELECT row_number()over(partition by BaseDirID order by Len(DisplayPath) DESC)rn,* 
       FROM existing_hierachies) a 
       CROSS apply dbo.[Delimitedsplit8k](DisplayPath, '\') 
       Where Rn = 1), 
    rec_cte 
    AS (SELECT *, 
       Cast(fol AS VARCHAR(4000))AS hierar 
     FROM folders_list 
     WHERE ItemNumber = 1 
     UNION ALL 
     SELECT d.*, 
       Cast(rc.hierar + '\' + d.fol AS VARCHAR(4000)) 
     FROM rec_cte rc 
       JOIN folders_list d 
        ON rc.BaseDirID = d.BaseDirID 
        AND d.ItemNumber = rc.ItemNumber + 1) 
SELECT rc.BaseDirID, 
     rc.hierar AS Missing_Hierarchies 
FROM rec_cte rc 
WHERE NOT EXISTS (SELECT 1 
        FROM existing_hierachies eh 
        WHERE eh.BaseDirID = rc.BaseDirID 
        AND eh.DisplayPath = rc.hierar) 
Order by rc.BaseDirID 

Ergebnis:

+-----------+---------------------+ 
| BaseDirID | Missing_Hierarchies | 
+-----------+---------------------+ 
|   1 | A\B     | 
|   1 | A\B\C\D    | 
|   4 | U\V     | 
|   4 | U\V\W\X    | 
+-----------+---------------------+ 

Split String Funktionscode

CREATE FUNCTION [dbo].[DelimitedSplit8K] 
     (@pString VARCHAR(8000), @pDelimiter CHAR(1)) 
RETURNS TABLE WITH SCHEMABINDING AS 
RETURN 
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000... 
    -- enough to cover NVARCHAR(4000) 
    WITH E1(N) AS (
       SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
       SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
       SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
       ),       --10E+1 or 10 rows 
     E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows 
     E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max 
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front 
        -- for both a performance gain and prevention of accidental "overruns" 
       SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4 
       ), 
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter) 
       SELECT 1 UNION ALL 
       SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter 
       ), 
cteLen(N1,L1) AS(--==== Return start and length (for use in substring) 
       SELECT s.N1, 
         ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000) 
        FROM cteStart s 
       ) 
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found. 
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1), 
     Item  = SUBSTRING(@pString, l.N1, l.L1) 
    FROM cteLen l 
; 
GO 

Bezogen von http://www.sqlservercentral.com/articles/Tally+Table/72993/

+0

Ich habe keine Ordnerliste. Was ich habe, sind die "root" -Ordner und die "leafs" -Ordner, die ich brauche, um die "Lücken" zwischen ihnen in der Hierarchie zu finden. –

+0

@NurielZrubavely - aktualisiert meine Antwort –

+0

Danke für Ihre Lösung. Von dem, was ich sah, funktioniert es nur für eine Hierarchie, aber wenn unsere Tabelle mehr als eine Hierarchie enthält, wird es die "Lücken" für die anderen Hierarchien nicht finden. Wenn beispielsweise die Tabelle enthält die folgenden Pfade: 'A' 'A \ B \ C' 'X' 'X \ Y \ Z' Die aktuelle Lösung uns nur zurückkehren wird nur die erste " Lücke ": 'A \ B' aber nicht die zweite Lücke: 'X \ Y' Können Sie bitte Lösung für diesen Fall auch vorschlagen? Nochmals vielen Dank, Nuriel –

Verwandte Themen