2017-01-24 14 views
3

폴더 경로가있는 테이블이 있습니다. 계층 구조에서 해당 폴더 사이의 모든 "갭"을 찾아야합니다.SQL : 테이블에서 누락 된 계층 폴더 (경로) 찾기

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

이 표 이상 25 만 기록 폴더의가 포함되어 내가 계층 구조에서 다음 누락 된 폴더를 찾을 필요가

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

: 나는 테이블이 3 폴더가 포함되어있는 경우, 의미 , 그래서 우리는 가장 효율적인 방법을 추구, 그렇지 않으면 스크립트는 오랜 시간 동안 우리가 없어 시간이 걸릴 것입니다.

댓글 : 모든 폴더 목록이 없습니다. 내가 가지고있는 것은 "루트"폴더와 계층 구조에서 "갭"을 찾아야하는 "리프"폴더입니다.

둘째 코멘트 : 테이블 캔 하나의 계층 구조 이상 더 포함하고 우리는 계층 구조모두의 "차이"를 찾아야합니다. 그런데 두 개의 또 다른 int 열이 있습니다 : "DirID"및 "BaseDirID". "DirID"열은 우리 테이블의 ID 열입니다. "BaseDirID"에는 계층 구조의 첫 번째 폴더의 ID가 포함됩니다. 따라서 동일한 계층 구조의 모든 폴더 (경로)는이 열에서 동일한 값을 공유합니다. 예를 들어, 샘플 데이터 :

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' 

그래서 우리는 다음과 같은 데이터를 찾을 필요가 : 사전에

Expected Results

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

감사합니다.

+0

이것은 SQL 외부에서 수행 할 작업입니다. 이 경우 모든 프로그래밍 언어가 t-sql보다 나은 작업을 수행합니다. –

답변

2

여기 Recursive CTE 및 분할 문자열 함수를 사용하여 하나의 방법이다

;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 

결과 :

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

분할 문자열 기능 코드

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 

을 참조하십시오. http://www.sqlservercentral.com/articles/Tally+Table/72993/

+0

폴더 목록이 없습니다. 내가 가진 것은 "루트"폴더와 계층 구조에서 "갭"을 찾아야하는 "리프"폴더입니다. –

+0

@NurielZrubavely - 내 대답 업데이트 –

+0

해결책 주셔서 감사합니다. 내가 본 것은 하나의 계층 구조에 대해서만 작동하지만, 테이블에 둘 이상의 계층 구조가 포함되어 있으면 다른 계층 구조에 대한 "갭"을 찾지 못합니다.예를 들어 테이블은 다음 경로를 포함하는 경우 : 'A' 'A \ B 형 \의 C' 'X' 'X Y \ Z는 \' 현재 해결책은 단지 첫 번째 우릴 반환 " 갭 ": 'A \ B' 'X \ Y' 두 번째 간격은 없습니다.이 경우에도 해결책을 제안 할 수 있습니까? 다시 감사, Nuriel –