56

我需要以特定方式获得树的有序层次结构。有问题的表看起来有点像这样(所有 ID 字段都是唯一标识符,为了举例,我已经简化了数据):

EstimateItemID EstimateID ParentEstimateItemID ItemType
-------------- ---------- -------- ------ --
       1 一个空产品
       2个1个产品
       3一2服务
       4 空产品
       5一4产品
       6一5服务
       7一1服务
       8一4产品

树结构的图形视图(* 表示“服务”):

           一个
       ___/ \___
      / \
    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' 的孩子,但是按照它出现在表格中的顺序。IE:

估计物品 ID
--------------
      1
      2
      3
      4
      5
      6
      7
      8

不幸的是,我需要的是一个有序的层次结构,其结果集遵循以下约束:

1.每个分支都要分组
2. ItemType 'product' 和 parent 的记录是顶部节点
3. 具有 ItemType 'product' 和非 NULL 父级的记录分组在顶部节点之后
4. ItemType 'service' 的记录是分支的底部节点

因此,在此示例中,我需要结果的顺序是:

估计物品 ID
--------------
      1
      2
      3
      7
      4
      5
      8
      6

我需要在查询中添加什么来完成此操作?

4

3 回答 3

91

尝试这个:

;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

With Path- rows a 按父节点排序

如果您想按每个级别对子节点进行排序ItemType,则可以使用Leveland SUBSTRINGof Pathcolumn....

这里有数据样本的SQLFiddle

于 2013-08-07T19:14:07.677 回答
21

这是对 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;
于 2016-10-29T20:19:18.890 回答
0

我认为您需要在 CTE 的结果中添加以下内容...

  1. BranchID = 某种唯一标识分支的标识符。请原谅我没有更具体,但我不确定是什么确定了满足您需求的分支。您的示例显示了一个二叉树,其中所有分支都流回根。
  2. ItemTypeID 其中(例如)0 = 产品和 1 = 服务。
  3. Parent = 标识父级。

如果输出中存在这些,我认为您应该能够将查询的输出用作另一个 CTE 或查询中的 FROM 子句。按 BranchID、ItemTypeID、Parent 排序。

于 2013-08-07T15:35:18.307 回答