公共表表达式遍历层次结构

时间:2016-12-09 16:26:13

标签: sql sql-server

结构

我有两个相互链接的表。一个是一组值和一个可空的外键,它指向另一个表的Id,其中包含 2 外键返回到另一个表。

HierarchicalTable
Id    LeftId    RightId    SomeValue
1     1         2          some value
2     3         4          top level in tree
3     5         6          incorrect hierarchy 1
4     7         8          incorrect result top level

IntermediateTable
Id   SomeValue             HierarchicalTableId
1    some value            NULL
2    value                 NULL
3    NULL                  1
4    value                 NULL
5    incorrect result 1    NULL
6    incorrect result 3    NULL
7    incorrect result 3    NULL
8    NULL                  3

每个表都指向 down 层次结构。以下是针对分层表记录1& 2及其IntermediateTable值:

(H : HierarchicalTable, I : IntermediateTable)

                    H-2
                   /   \
                 I-3    I-4
               /     
             H-1 
           /    \
         I-1    I-2

问题

我需要能够为给定的HierarchicalTable发送Id并获取其下的所有HierarchicalTable记录。因此,对于上面的结构,如果我将1传递给查询,我应该得到H-1(从那里,我可以加载相关的IntermediateTable值)。如果我通过2,我应该得到H-2和H-1(并再次使用它们来加载相关的IntermediateTable值。)

尝试

我尝试过使用CTE,但有些主要内容与我见过的例子不同:

  • 在我的结构中,对象将向下指向他们的孩子,而不是向上到他们的孩子
  • 我有 top 对象的ID,而不是 bottom 对象的ID。
  • 我的层次结构分为两个表。一旦我理解了算法以找到我需要的结果,这个就不应该是一个大问题,但这可能会给我带来额外的困惑。

如果我运行此查询:

declare @TargetId bigint = 2
;
with test as (
    select h.*
    from dbo.hierarchicaltable h
    inner join dbo.intermediatetable i
    on (h.leftid = i.id or h.rightid = i.id)

    union all

    select h.*
    from dbo.hierarchicaltable h
    where h.id = @TargetId
)
select distinct *
from test

我在HierarchicalTable中获得了所有4条记录,而不仅仅是记录1& 2.我不确定我想要的是否可以用CTE。

1 个答案:

答案 0 :(得分:1)

试试这个: 我用两个表构建整个树,然后过滤(只有分层表记录)。

DECLARE @HierarchicalTable TABLE(
    Id          INT,
    LeftId      INT,
    RightId     INT,
    SomeValue   VARCHAR(MAX)
)
INSERT INTO @HierarchicalTable
VALUES                                                    
(1,     1,         2,          'some value                '),
(2,     3,         4,          'top level in tree         '),
(3,     5,         6,          'incorrect hierarchy 1     '),
(4,     7,         8,          'incorrect result top level')

DECLARE @IntermediateTable TABLE(
    Id                      INT,
    SomeValue               VARCHAR(MAX),
    HierarchicalTableId     INT
)
INSERT INTO @IntermediateTable
VALUES
(1,    'some value'            ,NULL  ),
(2,    'value '                ,NULL  ),
(3,    NULL                    ,1     ),
(4,    'value '                ,NULL  ),
(5,    'incorrect result 1'    ,NULL  ),
(6,    'incorrect result 3'    ,NULL  ),
(7,    'incorrect result 3'    ,NULL  ),
(8,    NULL                    ,3     )

DECLARE @TargetId INT = 2;

WITH CTE AS (
    SELECT Id AS ResultId, LeftId, RightId, NULL AS HierarchicalTableId
    FROM @HierarchicalTable         
    WHERE Id = @TargetId

    UNION ALL

    SELECT C.Id AS ResultId, C.LeftId, C.RightId, NULL AS HierarchicalTableId 
    FROM @HierarchicalTable C
    INNER JOIN CTE          P ON P.HierarchicalTableId = C.Id

    UNION ALL

    SELECT NULL AS ResultId, NULL AS LeftId, NULL AS RightId, C.HierarchicalTableId 
    FROM @IntermediateTable C
    INNER JOIN CTE          P ON P.LeftId = C.Id OR P.RightId = C.Id
)
SELECT * 
FROM CTE
WHERE ResultId IS NOT NULL