2010-06-16 67 views
1

基于如下表递归排序T-SQL查询

ID Path  
--------------------------------------- 
1 \\Root 
2 \\Root\Node0 
3 \\Root\Node0\Node1 
4 \\Root\Node0\Node2 
5 \\Root\Node3 
6 \\Root\Node3\Node4 
7 \\Root\Node5 
... 
N \\Root\Node5\Node6\Node7\Node8\Node9\Node10 

等上......

有此表中约1000行。我想在单独的列中显示单个节点。要显示的最大列数5(即直到5级深的节点)。所以输出 看起来如下

ID Path   Level 0 Level 1 Level 2 Level 3 Level 4 Level 5 
---------------------------------------------------------------------------------------- 
1 \\Root     Root  Null  Null  Null  Null  Null 
2 \\Root\Node0    Root  Node 0 Null  Null  Null  Null 
3 \\Root\Node0\Node1  Root  Node 0 Node 1 Null  Null  Null 
4 \\Root\Node0\Node2  Root  Node 0 Node 2 Null  Null  Null 
5 \\Root\Node3    Root  Node 3 Null  Null  Null  Null 
6 \\Root\Node3\Node4  Root  Node 3 Node 4 Null  Null  Null 
7 \\Root\Node5    Root  Node 5 Null  Null  Null  Null 
... 
N (see in above table)  Root  Node 5 Node 6 Node 7 Node 8 Node 9 

我能想到的唯一方法是通过每一行打开游标,循环和执行字符串分割,只取前5个节点,然后插入到一个临时表。

请建议。

感谢

+0

SQL Server 2008具有hierarchyId(http://technet.microsoft.com/zh-cn/library/bb677173.aspx)。完全推荐。它支持像GetLevel和GetAncestor这样的有用方法,它们可以一起用来解决这个问题。当然,这会作为一个答案作弊,因为它改变了输入表格架构:-) – 2010-06-17 05:29:34

回答

0

你需要的是一个表值分割功能类似于如下:

CREATE FUNCTION [dbo].[udf_Split] (@DelimitedList nvarchar(max), @Delimiter nvarchar(2) = ',') 
RETURNS @SplitResults TABLE (Position int NOT NULL PRIMARY KEY, Value nvarchar(max)) 
AS 
Begin 
    Declare @DelimiterLength int 
    Set @DelimiterLength = DataLength(@Delimiter)/2 

    If Left(@DelimitedList, @DelimiterLength) <> @Delimiter 
     Set @DelimitedList = @Delimiter + @DelimitedList 

    If Right(@DelimitedList, @DelimiterLength) <> @Delimiter 
     Set @DelimitedList = @DelimitedList + @Delimiter 

    Insert @SplitResults(Position, Value) 
    Select CharIndex(@Delimiter, A.list, N.Value) + @DelimiterLength    
     , Substring (
        A.List 
        , CharIndex(@Delimiter, A.list, N.Value) + @DelimiterLength   
        , CharIndex(@Delimiter, A.list, N.Value + 1)        
         - (CharIndex(@Delimiter, A.list, N.Value) + @DelimiterLength) 
        ) 
    From dbo.Numbers As N 
     Cross Join (Select @DelimitedList As list) As A 
    Where N.Value > 0 
     And N.Value < LEN(A.list) 
     And Substring(A.list, N.Value, @DelimiterLength) = @Delimiter 
    Order By N.Value 

    Return 
End 

此功能依赖于它包含整数的顺序列表Numbers表的存在。现在,您可以获取原始数据并执行类似操作:

With TableData As 
    (
    Select 1 As Id, '\\Root' As [Path] 
    Union Select All 2, '\\Root\Node0' 
    Union Select All 3, '\\Root\Node0\Node1' 
    Union Select All 4, '\\Root\Node0\Node2' 
    Union Select All 5, '\\Root\Node3' 
    Union Select All 6, '\\Root\Node3\Node4' 
    Union Select All 7, '\\Root\Node5' 
    ) 
    , SplitData As 
    (
    Select T.Id, T.[Path], S.Value 
     , Row_Number() Over (Partition By T.Id Order By S.Position) As Level 
    From TableData As T 
     Cross Apply dbo.udf_Split((Substring(T.[Path],2,Len(T.[Path])) + '\') , '\') As S 
    ) 
Select Id, [Path] 
    , Min(Case When Level = 1 Then S.Value End) As Level0 
    , Min(Case When Level = 2 Then S.Value End) As Level1 
    , Min(Case When Level = 3 Then S.Value End) As Level2 
    , Min(Case When Level = 4 Then S.Value End) As Level3 
    , Min(Case When Level = 5 Then S.Value End) As Level4 
From SplitData As S 
Group By Id, [Path] 
+0

谢谢托马斯。那么我应该创建一个临时Numbers表吗? Numbers表中包含多少行? – stackoverflowuser 2010-06-16 21:23:41

+0

@stackoverflowuser - 表没有理由永久不变。 Numbers表有很多用途,这只是其中之一。如果您注意到在Split函数中,它会过滤字符串的长度,所以数字表格应至少与最大字符串一样大。但是,10K行将占用很小的空间,应该足够满足您的需求。 – Thomas 2010-06-16 21:25:44

+0

嗨托马斯,我无法通过“路径”(输入表中的列)作为输入到dbo.udf_Split。它给了我无效的列错误。 “元素”列也来自哪里?谢谢。 – stackoverflowuser 2010-06-16 23:27:40