SQLUSA

Microsoft SQL Server 2005 Articles

LAUNCH CELEBRATION ARTICLE

 

Directory Tree

By Kalman Toth, M.Phil., M.Phil., MCDBA, MCITP

November 7, 2005

The incredible power of SQL Server 2005 is demonstrated below by a stored procedure which maps a file system directory. It applies a recursive CTE to help in the mapping. The levels go to 12, but that can easily be extended if needed.


This is the code sample:

USE AdventureWorks;
GO

drop proc DirTree
go
CREATE PROC DirTree @Folder varchar(100)
AS
BEGIN
-- Source: www.sqlusa.com
-- Author: Kalman Toth
-- November 7, 2005


-- Get row folder map into a temporary table
CREATE TABLE #DirTree (ID int IDENTITY(1,1),
SubDir varchar(100), Depth int, ParentID int default(1))
INSERT #DirTree (SubDir, Depth, ParentID)
SELECT @Folder,0,0
INSERT #DirTree(SubDir,Depth)
EXEC master.sys.xp_dirtree @Folder

-- Update ParentID
UPDATE #DirTree set ParentID=b.ID
FROM #DirTree, #DirTree b
WHERE #DirTree.ID > b.ID
and b.ID = (SELECT max(ID) FROM #DirTree c
WHERE c.ID < #DirTree.ID and #DirTree.Depth = c.Depth+1);

-- Create a table for results
IF NOT EXISTS (SELECT 1 FROM dbo.sysobjects
WHERE id = OBJECT_ID(N'[dbo].[DirectoryTree]')
AND OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN
CREATE TABLE [dbo].[DirectoryTree](
[DirectoryTreeID] [int] IDENTITY(1,1) NOT NULL,
[Root] [varchar](100) NULL,
[Level1] [varchar](100) NOT NULL,
[Level2] [varchar](100) NOT NULL,
[Level3] [varchar](100) NOT NULL,
[Level4] [varchar](100) NOT NULL,
[Level5] [varchar](100) NOT NULL,
[Level6] [varchar](100) NOT NULL,
[Level7] [varchar](100) NOT NULL,
[Level8] [varchar](100) NOT NULL,
[Level9] [varchar](100) NOT NULL,
[Level10] [varchar](100) NOT NULL,
[Level11] [varchar](100) NOT NULL,
[Level12] [varchar](100) NOT NULL
) ON [PRIMARY]
END ;

DELETE DirectoryTree WHERE Root=@Folder;

-- Recursive CTE
WITH cteDirTree(FolderID, Name, ParentID, Level)
AS(
-- Anchor Member (AM)
SELECT ID, SubDir, ParentID, 0
FROM #DirTree
WHERE ParentID = 0
UNION ALL
-- Recursive Member (RM)
SELECT ID, SubDir, D.ParentID, C.Level+1
FROM #DirTree D
JOIN cteDirTree AS C
ON D.ParentID = C.FolderID)


-- Tree builder from one level to next
INSERT INTO [AdventureWorks].[dbo].[DirectoryTree]([Root],[Level1],[Level2],[Level3],
[Level4],[Level5],[Level6],[Level7],[Level8],[Level9],
[Level10],[Level11],[Level12])
SELECT Root=l0.Name, Level1=isnull(l1.Name, ''),
Level2=isnull(l2.Name,''), Level3=isnull(l3.Name,''), Level4=isnull(l4.Name,''),
Level5=isnull(l5.Name,''),Level6=isnull(l6.Name,''), Level7=isnull(l7.Name,''),
Level8=isnull(l8.Name,''), Level9=isnull(l9.Name,''), Level10=isnull(l10.Name,''),
Level11=isnull(l11.Name,''), Level12=isnull(l12.Name,'')
FROM
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=0) l0
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=1) l1
ON l1.ParentID =l0.FolderID
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=2) l2
ON l2.ParentID = l1.FolderID
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=3) l3
ON l3.ParentID = l2.FolderID
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=4) l4
ON l4.ParentID = l3.FolderID
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=5) l5
ON l5.ParentID = l4.FolderID
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=6) l6
ON l6.ParentID = l5.FolderID
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=7) l7
ON l7.ParentID = l6.FolderID
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=8) l8
ON l8.ParentID = l7.FolderID
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=9) l9
ON l9.ParentID = l8.FolderID
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=10) l10
ON l10.ParentID = l9.FolderID
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=11) l11
ON l11.ParentID = l10.FolderID
LEFT JOIN
(SELECT Name, FolderID, ParentID FROM cteDirTree where Level=12) l12
ON l12.ParentID = l11.FolderID
ORDER BY l1.Name, l2.Name, l3.Name, l4.Name, l5.Name, l6.Name, l7.Name,
l8.Name, l9.Name, l10.Name, l11.Name, l12.Name

SELECT * FROM DirectoryTree WHERE ROOT=@Folder ORDER BY DirectoryTreeID
END
go

EXEC DirTree 'c:\Program Files\'
go


This is the partial result set:


ID Root Level1 Level2 Level3 Level4 Level5
25 c:\Program Files\ Adobe Acrobat 5.0 Reader Optional  
26 c:\Program Files\ Adobe Acrobat 5.0 Reader plug_ins InterTrust
27 c:\Program Files\ Adobe Acrobat 5.0 Reader plug_ins Movie
28 c:\Program Files\ Adobe Acrobat 5.0 Reader plug_ins vdkhome enu
29 c:\Program Files\ Adobe Acrobat 5.0 Reader plug_ins WEBBUY HTML

The fnPath function returns a path based on the directory id in DirectoryTree table:


CREATE FUNCTION fnPath (@DirectoryTreeID int)
RETURNS varchar(2048)
AS
BEGIN
DECLARE @Path varchar(2048)
SELECT @Path=Root+Level1+'\'+Level2+'\'+Level3+'\'+Level4+'\'+
Level5+'\'+Level6+'\'+Level7+'\'+Level8+'\'+Level9+
'\'+Level10+'\'+Level11+'\'+Level12+'\'
FROM dbo.DirectoryTree
WHERE DirectoryTreeID=@DirectoryTreeID
WHILE ( right(@Path,2)='\\' ) SET @Path=left(@Path, len(@Path)-1)
RETURN @Path
END
GO
SELECT dbo.fnPath (10)
GO

The ListFilesInSubfolder stored proc returns the list of files in a subfolder:


CREATE PROC ListFilesInSubfolder @DirectoryTreeID int
AS
BEGIN
DECLARE @Path varchar(2048)
SELECT @Path = 'dir "'+dbo.fnPath ( @DirectoryTreeID)+'" /A-D /B'
CREATE TABLE #FileList ( Line varchar(512))
INSERT #FileList
EXEC master.dbo.xp_cmdshell @Path
SELECT * FROM #FileList WHERE Line is NOT NULL
END
GO

EXEC dbo.ListFilesInSubfolder 200
GO


 

The World Leader in SQL Server Training
 
SQLUSA.com Home Page