CTE递归以使用“空级别”获得层次结构级别

时间:2019-02-07 23:45:04

标签: sql sql-server

我在SQL Server 2012中具有下面的表结构

INSERT INTO tblLocations (Relation, ParentID, Name, Levelnumber, Code) 
VALUES
    ('Parent', NULL, 'West',0,'X'),
    ('Child', 1, 'WA',1,'X'),
    ('Grandchild', 2, 'Seattle',2,'X'),
    ('Grandchild', 2, 'Seattle2',2,'X'),
    ('GreatGrandchild', 3, 'aa',3,'X'),
    ('GreatGrandchild', 3, 'bb',3,'X'),
    ('Parent', NULL, 'MidWest',0,'Y'),
    ('Child', 7, 'IL',1,'Y'),
    ('Grandchild', 8, 'Chicago',2,'Y'),
    ('Grandchild',8, 'Chicago1',2,'Y'),
    ('GreatGrandchild', 9, 'cc',3,'Y'),
    ('GreatGrandchild', 9, 'dd',3,'Y'),
    ('Parent', NULL, 'South',0,'Z'),
    ('Child', 13, 'TX',1,'Z'),
    ('GreatGrandchild', 14, 'ff',3,'Z'),
    ('GreatGrandchild', 14, 'ff',3,'Z'),
    ('Parent', NULL, 'North',0,'A'),
    ('Grandchild',17, 'Chicago1',2,'A'),
    ('GreatGrandchild', 18, 'ff',3,'A'),
    ('GreatGrandchild', 18, 'ff',3,'A');

我们可以看到某些级别对于所有节点都不存在。我们的要求是获取所有节点的所有级别。

例如,对于代码X

Parent -> Child -> GrandChild --> GreatGrandchild 

层次结构存在..但是对于代码A,我们有

Parent -> GrandChild -> GreatGrandChild

类似于代码Z:

Parent -> Child -> GreatGrandChild

我们的要求是,如果不存在一个级别,则应为缺少的级别填充下一个级别(由级别编号标识)。我们只有4个级别。.

我们总是需要

Parent -> Child (if child is not there search down (Grandchild / GreatGrandchild) until data is there and populate as Child) 
       -> GrandChild - > GreatGrandChild

这是我们获得的CTE,

WITH Hierarchy AS 
( 
    -- initialization 
    SELECT Relation, ParentID, Name, Levelnumber, Code
    FROM tblLocations
    WHERE LevelNumber = 0

    UNION ALL 

    -- recursive execution 
    SELECT S.Relation, S.ParentID,S.Name,S.Levelnumber, S.Code
    FROM tblLocations S 
    INNER JOIN tblLocations T ON T.Id = S.ParentId 
) 
SELECT * 
FROM Hierarchy 
WHERE Code = 'X' ;

在视图中需要它,因此不需要T-SQL。

友好的指导

1 个答案:

答案 0 :(得分:0)

您的递归查询存在问题,因为它不是递归的。根据定义,递归查询是自引用的。您编写查询的方式,应定义递归的部分只是表与其自身的常规连接。

也就是说,一切并没有丢失。这是我想出的:

WITH Hierarchy AS 
( 
--initialization 
SELECT ID, 
    Relation, 
    ParentID, [Name], 
    Levelnumber, 
    Code, 
    1 AS [__level__],
    cast(concat('/', ID, '/') as varchar(max)) as h
FROM tblLocations
WHERE LevelNumber = 0
UNION ALL 
--recursive execution 
SELECT child.ID,
    child.Relation,
    child.ParentID, 
    child.Name, 
    child.Levelnumber, 
    child.Code, 
    parent.[__level__] + 1,
    cast(concat(parent.h, child.ID, '/') as varchar(max)) as h
FROM tblLocations child
INNER JOIN Hierarchy AS parent
    ON parent.Id = child.ParentId 
) 
SELECT *, 
    choose(
        Hierarchy.[__level__], 
        'Parent', 
        'Child', 
        'GrandChild', 
        'GreatGrandchild'
    ) as [DerivedRelation]
FROM Hierarchy 
WHERE Code = 'A' 
order by h;

实际的递归查询是相当标准的层次结构遍历。为了满足您的要求,我正在计算自己在层次结构中所处位置的概念,以便可以使用它来确定要显示为关系的内容。您没有说您正在使用什么版本的SQL,因此您可能没有concat()choose()。没关系;它们只是string + stringcase语句周围的语法糖。

我在这里还要注意的另一件事是h列。我已经写了相当一部分分层查询,对于它们的实际运行时执行,我更喜欢hierarchyid。您在维护数据上花了一点钱,但是与它们一起进行查询是相当有效的(因为您可以索引层次结构并说诸如where h.IsDescendentOf(some_other_hierarchyID)之类的东西。所有这些就是说h如果您想朝该方向前进,则该列可以直接转换为architectureid。