2013-08-07 194 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