Sql server 获取树层次结构的CTE递归

Sql server 获取树层次结构的CTE递归,sql-server,common-table-expression,Sql Server,Common Table Expression,我需要以特定的方式获得树的有序层次结构。该表看起来有点像这样(所有ID字段都是唯一标识符,为了举例,我简化了数据): 这给了我EstimateID“A”的子项,但按它在表中出现的顺序。即: EstimateItemID -------------- 1 2 3 4 5 6 7 8 估计项目ID -------------- 1. 2. 3. 4. 5. 6. 7. 8. 不幸的是,我需要的是一个有序的

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

这给了我EstimateID“A”的子项,但按它在表中出现的顺序。即:

EstimateItemID -------------- 1 2 3 4 5 6 7 8 估计项目ID -------------- 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 1.每个分支必须分组 2.ItemType为“product”且父项为的记录是顶部节点 3.ItemType为“product”且父级非空的记录分组在顶部节点之后 4.ItemType为“service”的记录是分支的底部节点 因此,在本例中,我需要结果的顺序是:

EstimateItemID -------------- 1 2 3 7 4 5 8 6 估计项目ID -------------- 1. 2. 3. 7. 4. 5. 8. 6.
我需要在我的查询中添加什么才能完成此任务?

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

  • BranchID=唯一标识分支的某种标识符。请原谅我没有说得更具体,但我不确定是什么根据您的需要确定了分支机构。您的示例显示了一个二叉树,其中所有分支都流回到根
  • ItemTypeID,其中(例如)0=产品,1=服务
  • 父项=标识父项
  • 如果这些存在于输出中,我认为您应该能够将查询的输出用作另一个CTE或查询中的from子句。按BranchID、ItemTypeID、父项排序。

    尝试以下操作:

    ;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
    
    使用路径-按父节点排序的行a

    如果要按每个级别的
    ItemType
    对子节点排序,则可以使用
    Path
    列的
    level
    SUBSTRING


    下面是数据示例

    这是法比奥从上面提出的伟大想法的补充。就像我在回复他原来的帖子时说的。我重新发布了他的想法,使用了更常见的数据、表名和字段,使其他人更容易理解

    谢谢你,法比奥!顺便说一句,这是个好名字

    首先是一些需要处理的数据:

    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;
    

    分支的根将由ParentEstimateItemID为NULL的记录标识。因此,“1”下的所有内容都是分支x,而“4”下的所有内容都是分支y。我对sql不太熟练,正在学习CTE,所以请原谅我。您的观点需要添加到第一个SELECT语句中吗?太棒了。这已经有几年的历史了,但今天发现它很有用。不过,恕我直言,我发现原始帖子中提供的示例很难转化为更常见的解决方案。因此,我使用更常见的数据、表名和字段重新发布了您的(很棒的)想法,以便于其他人遵循。是否有任何方法可以对级别为0的ItemType进行排序,层次结构应保持原样?使用SQL server 2017(和ssms17)中的上述代码,我在使用的第二个级别中得到错误“无效列名‘级别’”(级别+1)。知道如何修复它吗?@farshad,这很有效。还有一件事要提,如果您想在树上或树下,请将连接中的父/子列切换到cte.cte.child=table。parent向上,cte.parent=table.child向下
    ;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
    
    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;