2014-11-20 62 views
4

我在SQL Server中有一個層次結構,有多個父級,但似乎無法獲得我需要的結果集。在SQL層次結構中顯示所有子孫級CTE

這就是我到目前爲止。

DECLARE @Table TABLE (ChildId varchar(max), ParentId varchar(max)) 
INSERT INTO @Table (ChildId,ParentId) 
VALUES 
     ('England',NULL), 
     ('Cities',NULL), 
     ('Towns',NULL), 
     ('South West','England'), 
     ('Bristol','South West'), 
     ('Bristol','Cities'), 
     ('Suburb','Bristol'), 
     ('Thornbury','South West'), 
     ('Thornbury','Towns'); 


WITH CTE (ChildId, ParentId, Level) 
AS ( 
     SELECT 
      ChildId, 
      ParentID, 
      0 
     FROM @Table 
     WHERE ParentID IS NULL 
     UNION ALL 

     SELECT 
      r.ChildId, 
      r.ParentId, 
      ct.Level + 1 
     FROM @Table r 
     JOIN CTE ct 
     ON ct.ChildId = r.ParentId 

    ) 

SELECT * FROM CTE order by childId, level 

,給了我這樣的結果集:

ChildId | ParentId | Level 
Bristol | Cities  | 1 
Bristol | South West | 2 
Suburb  | Bristol | 2 
Suburb  | Bristol | 3 
Cities  | NULL  | 0 
England | NULL  | 0 
South West | England | 1 
Thornbury | Towns  | 1 
Thornbury | South West | 2 
Towns  | NULL  | 0 

但我也想祖父母和偉大的祖父母和偉大的祖父母(等):

ChildId | ParentId | Level 
Bristol | Cities  | 1 
Bristol | South West | 2 
Bristol | England | <------------------------ 
Suburb  | South West | <------------------------ 
Suburb  | England | <------------------------ 
Suburb  | Cities  | <------------------------ 

+0

可能重複的[如何從SQL表中檢索分層數據?](http://stackoverflow.com/questions/11230693/how -to-檢索分層數據-從-A-SQL的表)。查看該QA中的選定答案。可能是你正在尋找與CTE的 – 2014-11-20 16:56:23

+0

不,這是一個標準的層次結構SQL問題,其中有很多例子。我特別需要所有鏈接(大兒童,大孫子)喜歡的問題狀態 - 請刪除重複的標誌 – 2014-11-20 16:59:45

+0

可能重複的[Select語句返回父母和無限兒童](http://stackoverflow.com/questions/25550850/select-statement-to-return-parent-and-infinite-children) – Tanner 2014-11-20 17:00:58

回答

4

什麼你正在嘗試做的似的,至少在一定程度上,阮岡納讚的分類。在這種情況下,你必須在層次結構上,不下來:

with cte as (
    select t.ChildId, t.ParentId, 0 as [Lvl] 
    from @Table t 
    where t.ParentId is not null 
    union all 
    select c.ChildId, t.ParentId, c.Lvl + 1 
    from @Table t 
     inner join cte c on c.ParentId = t.ChildId 
    where t.ParentId is not null 
) 
select * from cte c order by c.ChildId, c.Lvl, c.ParentId; 

編輯:在CTE的遞歸部分更新WHERE條款。看起來像是從最初的嘗試剩下的一些,我忘了想通過..

+0

幹得好!比我的更簡潔,可能更有效的解決方案。儘管我在where子句中無法理解c.ParentId!= t.ParentId的用途。 – 2014-11-21 10:03:02

+0

@GiorgosBetsos,你是對的。我更新了答案。 – 2014-11-21 15:13:38

+0

現在很好! – 2014-11-21 15:16:10

0

如何使用遞歸表值函數而不是CTE:

CREATE FUNCTION tvf_GetParents 
( 
    @childID VARCHAR(MAX), 
    @level INT 
) 
RETURNS 
@output TABLE 
(
    ancestor VARCHAR(MAX), 
    level INT 
) 
AS 
BEGIN 

    DECLARE @parentIDs TABLE (pID VARCHAR(MAX)) 

    -- Get parent of child and add it to output 
    IF EXISTS (SELECT 1 FROM HTable WHERE ChildId = @childID AND ParentId IS NOT NULL) 
    BEGIN 
     INSERT @parentIDs 
     SELECT ParentId FROM HTable WHERE ChildId = @childID 

     INSERT INTO @output (ancestor, level) 
     SELECT pID, @level FROM @parentIDs 
    END  
    ELSE 
     RETURN 

    DECLARE @pID VARCHAR(MAX) = 0 

    -- Iterate over all parents (cursorless loop) 
    WHILE (1 = 1) 
    BEGIN 

     -- Get next ParentId 
     SELECT TOP 1 @pID = pID 
     FROM @parentIDs 
     WHERE pID > @pID 
     ORDER BY pID 

     -- Exit loop if no more parents 
     IF @@ROWCOUNT = 0 BREAK; 

     -- call function recursively so as to add to output 
     -- the rest of the ancestors (if any) 
     INSERT INTO @output (ancestor, level) 
     SELECT ancestor, level FROM tvf_GetParents(@pID, @level + 1) 
    END 

    RETURN 
END 
GO 

使用上述功能,您可以輕鬆地獲得所有子 - 祖先對:

SELECT DISTINCT ChildId, ancestor, level 
FROM HTable h 
OUTER APPLY tvf_GetParents(h.ChildId, 0) AS p 
ORDER BY ChildId, Level 

輸出:

ChildId ancestor  level 
------------------------------ 
Bristol Cities  0 
Bristol South West 0 
Bristol England  1 
Cities  NULL   NULL 
England NULL   NULL 
South West England  0 
Suburb  Bristol  0 
Suburb  Cities  1 
Suburb  South West 1 
Suburb  England  2 
Thornbury South West 0 
Thornbury Towns  0 
Thornbury England  1 
Towns  NULL   NULL 

請注意, '等級' 有不同的含義在這裏:水平NULL表示無父母子女,等級0表示父母親記錄,等級1表示子女祖父母記錄等等。

請注意,就sql server中的遞歸函數的嵌套級別而言,存在限制。我認爲它是32.如果你的樹深度超出了這個範圍,那麼我提出的解決方案將無法工作。

+0

謝謝,但更喜歡CTE版本,更簡潔和@Roger狼已經給出瞭解決方案 – 2014-11-21 09:50:31

-3

這是你在找什麼?

WITH CTE (ChildId, FirstChild, ParentId, Level) 
AS ( 
     SELECT 
      ChildId, 
      ChildId as FirstChild, 
      ParentID, 
      0 
     FROM @Table 
     WHERE ParentID IS NULL 
     UNION ALL 
     SELECT 
      r.ChildId, 
      ct.FirstChild, 
      r.ParentId, 
      ct.Level + 1 
     FROM @Table r 
     JOIN CTE ct 
     ON ct.ChildId = r.ParentId 

    ) 
SELECT ChildId, 
    ParentId, 
    Level 
FROM CTE 
UNION 
SELECT FirstChild, 
    ParentId, 
    Level 
FROM CTE 
ORDER BY ChildId, 
    Level, 
    ParentId 

輸出:

ChildId  ParentId  Level 
-------  --------  ----- 
Bristol  Cities  1 
Bristol  South West 2 
Cities  NULL   0 
Cities  Cities  1 
Cities  Bristol  2 
England  NULL   0 
England  England  1 
England  South West 2 
England  Bristol  3 
South West England  1 
Suburb  Bristol  2 
Suburb  Bristol  3 
Thornbury Towns  1 
Thornbury South West 2 
Towns  NULL   0 
Towns  Towns  1 
+0

我不認爲上述輸出是正確的一個:例如英格蘭是最高水平的紀錄。它應該只在輸出中出現一次。 – 2014-11-20 21:32:39