2013-08-07 236 views
27

我需要以特定方式獲得樹的有序層次結構。所討論的表看起來有點像這樣(所有ID字段uniqueidentifiers,我已經簡化數據例如起見):CTE遞歸獲取樹層次結構

EstimateItemID EstimateID ParentEstimateItemID  ItemType 
-------------- ---------- --------------------  -------- 
     1    A    NULL    product 
     2    A     1    product 
     3    A     2    service 
     4    A    NULL    product 
     5    A     4    product 
     6    A     5    service 
     7    A     1    service 
     8    A     4    product

樹結構的圖形視圖(*表示「服務」):

 
      A 
     ___/ \___ 
    /  \ 
    1   4 
/\  /\ 
    2 7*  5 8 
/  /
3*   6* 

使用此查詢,我可以得到層級(假裝「A」是唯一標識符,我知道這是不是在現實生活中):

DECLARE @EstimateID uniqueidentifier 
SELECT @EstimateID = 'A' 

;WITH temp as(
    SELECT * FROM EstimateItem 
    WHERE EstimateID = @EstimateID 

    UNION ALL 

    SELECT ei.* FROM EstimateItem ei 
    INNER JOIN temp x ON ei.ParentEstimateItemID = x.EstimateItemID 
) 

SELECT * FROM temp 

這給了我EstimateID的孩子'A',但是在它出現在表格中的順序。即:

EstimateItemID 
-------------- 
     1 
     2 
     3 
     4 
     5 
     6 
     7 
     8

不幸的是,我需要的是遵循以下限制的結果集的排序層次:

 
1. each branch must be grouped 
2. records with ItemType 'product' and parent are the top node 
3. records with ItemType 'product' and non-NULL parent grouped after top node 
4. records with ItemType 'service' are bottom node of a branch 

所以,爲了我需要的結果,在這個例子中,是:

EstimateItemID 
-------------- 
     1 
     2 
     3 
     7 
     4 
     5 
     8 
     6 

我需要添加到我的查詢來完成此操作?

回答

53

試試這個:

;WITH items AS (
    SELECT EstimateItemID, ItemType 
    , 0 AS Level 
    , CAST(EstimateItemID AS VARCHAR(255)) AS Path 
    FROM EstimateItem 
    WHERE ParentEstimateItemID IS NULL AND EstimateID = @EstimateID 

    UNION ALL 

    SELECT i.EstimateItemID, i.ItemType 
    , Level + 1 
    , CAST(Path + '.' + CAST(i.EstimateItemID AS VARCHAR(255)) AS VARCHAR(255)) 
    FROM EstimateItem i 
    INNER JOIN items itms ON itms.EstimateItemID = i.ParentEstimateItemID 
) 

SELECT * FROM items ORDER BY Path 

隨着Path - 行的父母排序節點

如果你想爲每個級別排序ItemType的childNodes,比你可以用LevelPathSUBSTRING玩。 ...

這裏SQLFiddle帶有樣本的數據

+0

輝煌。這已經有幾年了,但今天發現它很有用。然而,原諒說,我發現在原來的帖子中提供的例子很難讓我轉化爲更常見的解決方案。所以,我使用更常見的數據,表格名稱和字段來重新發布您的(偉大的)想法,以便其他人更容易遵循。 – ptownbro

+0

有沒有什麼辦法可以通過ItemType的級別0進行排序,並且層次結構應該保持原樣? –

0

我相信你需要添加以下到您的CTE的結果...

  1. BranchID =某種標識的唯一標識分支。原諒我不是更具體,但我不確定什麼標識分支爲您的需求。您的示例顯示了一個二叉樹,其中所有分支都流回根目錄。
  2. ItemTypeID其中(例如)0 =產品和1 =服務。
  3. 父母=識別父母。

如果那些在輸出中存在,我認爲你應該能夠使用查詢的輸出作爲另一個CTE或查詢中的FROM子句。按BranchID,ItemTypeID,Parent排序。

+0

分公司的根將用NULL ParentEstimateItemID記錄來識別

首先,一些數據一起工作。所以,'1'下的所有東西都是分支x,而4以下的東西都是分支y。我不熟悉sql,並且正在學習CTE,所以請原諒我。你的觀點是否需要添加到第一個SELECT語句中? – Woods8460

3

這是來自上面Fabio的好主意的插件。就像我在回覆他原來的帖子時所說的那樣。我已經使用更常見的數據,表名和字段重新發布了他的想法,以便其他人更容易遵循。

謝謝法比奧!偉大的名字。

CREATE TABLE tblLocations (ID INT IDENTITY(1,1), Code VARCHAR(1), ParentID INT, Name VARCHAR(20)); 

INSERT INTO tblLocations (Code, ParentID, Name) VALUES 
('A', NULL, 'West'), 
('A', 1, 'WA'), 
('A', 2, 'Seattle'), 
('A', NULL, 'East'), 
('A', 4, 'NY'), 
('A', 5, 'New York'), 
('A', 1, 'NV'), 
('A', 7, 'Las Vegas'), 
('A', 2, 'Vancouver'), 
('A', 4, 'FL'), 
('A', 5, 'Buffalo'), 
('A', 1, 'CA'), 
('A', 10, 'Miami'), 
('A', 12, 'Los Angeles'), 
('A', 7, 'Reno'), 
('A', 12, 'San Francisco'), 
('A', 10, 'Orlando'), 
('A', 12, 'Sacramento'); 

現在遞歸查詢:

-- Note: The 'Code' field isn't used, but you could add it to display more info. 
;WITH MyCTE AS (
    SELECT ID, Name, 0 AS TreeLevel, CAST(ID AS VARCHAR(255)) AS TreePath 
    FROM tblLocations T1 
    WHERE ParentID IS NULL 

    UNION ALL 

    SELECT T2.ID, T2.Name, TreeLevel + 1, CAST(TreePath + '.' + CAST(T2.ID AS VARCHAR(255)) AS VARCHAR(255)) AS TreePath 
    FROM tblLocations T2 
    INNER JOIN MyCTE itms ON itms.ID = T2.ParentID 
) 
-- Note: The 'replicate' function is not needed. Added it to give a visual of the results. 
SELECT ID, Replicate('.', TreeLevel * 4)+Name 'Name', TreeLevel, TreePath 
FROM MyCTE 
ORDER BY TreePath; 
+0

不錯的解決方案!我發佈了SQLFiddle,http://sqlfiddle.com/#!6/5e70c/2/0 – MAbraham1