返回父组件下多个级别的所有组件

Returning all components multiple levels under a parent component

我有一个分层数据库,我想从中 return 具有 特定 公共父组件的资产中的所有组件。

tableastComponents 中的组件通过 ParentComponentId 链接到它们的父组件,引用其他组件的 Id。然而,这只显示父级上一级。

此外,所有组件都通过 AssetId 键链接到资产,链接到 table astAssets.

中的 Id

我能够进行一个简单的查询(如下)来检索特定资产的所有组件。我不知道如何 return 来自共享相同父组件的特定资产的所有组件。此父组件可能(相对于子组件)1 到多个级别。

Select C.Id, C.Code, ParentC.Id, ParentC.Code, A.Code
From astComponents C

Join astAssets A ON A.Id = C.AssetId
Join astComponents ParentC ON ParentC.Id = C.ParentComponentId

Where A.Code = '2018100000'

以上查询 return 的组件及其上一级的父组件。如果我指定父代码,它只会 return 子组件比这个指定组件低一级,而不是所有级别。

所以如果我没有理解错的话,你有一个已知的父组件,一个已知的资产代码。使用它们,您想构建一个树,覆盖从父组件到相关资产的 路径 (缺少更好的词)?

这是一个尝试。我们首先构建一些用于测试的虚拟数据:

declare @component table (
    id int primary key,
    code nvarchar(50) not null,
    parent_id int null,
    asset_id int null
)
declare @asset table (
    id int primary key,
    code nvarchar(50)
)

insert into @asset values (1, '2018100000')
insert into @asset values (2, '2019100000')

insert into @component values(1, 'root', null, null)
insert into @component values(2, 'c1', 1, null)
insert into @component values(3, 'c2', 1, null)
insert into @component values(4, 'c3', 1, null)
insert into @component values(5, 'c2_c1', 3, null)
insert into @component values(6, 'c2_c2', 3, 1)
insert into @component values(7, 'c2_c1_c1', 5, 2)
insert into @component values(8, 'c2_c1_c2', 5, 1)
insert into @component values(9, 'c2_c1_c2_c1', 8, 2)
insert into @component values(9, 'c2_c1_c1_c1', 7, 1)

然后我们声明几个递归 CTE:s 并使用它们来获取输出:

declare @specParentId int
declare @assetCode nvarchar(50)
set @specParentId = 3
set @assetCode = '2018100000'

;with compRecurseDown as (
    select c.id, c.code, parent_id, 0 as level, c.asset_id
    from @component c
    where c.id = @specParentId

    union all

    select c.id, c.code, c.parent_id, p.level + 1, c.asset_id
    from @component c
        join compRecurseDown p on c.parent_id = p.id
), assetRecurseUp as (
    select c.*
    from @asset a
        join @component c on a.id = c.asset_id
    where a.code = @assetCode

    union all 

    select c.*
    from @component c
        join assetRecurseUp p on p.parent_id = c.id
)
select crd.*, a.code as asset_code
from compRecurseDown crd
    left outer join @asset a on crd.asset_id = a.id
where crd.id in (
    select id from assetRecurseUp
)
order by level

在这种情况下,我们要在 ID 为 3 的组件下查找资产 2018100000。结果:

| id | code     | parent_id | level | asset_id | asset_code | 
|----|----------|-----------|-------|----------|------------| 
| 3  | c2       | 1         | 0     | NULL     | NULL       | 
| 5  | c2_c1    | 3         | 1     | NULL     | NULL       | 
| 6  | c2_c2    | 3         | 1     | 1        | 2018100000 | 
| 8  | c2_c1_c2 | 5         | 2     | 1        | 2018100000 | 

那么这是如何工作的呢?首先我们声明 compRecurseDown CTE。它使用已知的父组件作为其anchor,然后递归查找该节点下的所有子注释。

assetRecurseUp CTE 首先查找所有链接到已知资产的组件。然后它基于此递归添加所有父节点。

最后我们查询这两者的组合,产生最终的结果集。

好的,没有任何数据可以使用,这里有很多猜测。我创建了一些数据:

DECLARE @astComponents TABLE (Id INT, Code VARCHAR(50), ParentComponentId INT, AssetId INT);
DECLARE @astAssets TABLE (Id INT, Code VARCHAR(50));
INSERT INTO @astAssets SELECT 1, '2018100000';
INSERT INTO @astAssets SELECT 2, '2018100001';
INSERT INTO @astComponents SELECT 1, 'Power Supply', NULL, 1;
INSERT INTO @astComponents SELECT 2, 'Fan', 1, NULL;
INSERT INTO @astComponents SELECT 3, 'Screw', 2, NULL;
INSERT INTO @astComponents SELECT 4, 'Heat Tape', 1, NULL;
INSERT INTO @astComponents SELECT 5, 'Magnet', NULL, 2;
INSERT INTO @astComponents SELECT 6, 'Iron', 5, NULL;

你可以用这个做的是使用递归映射出所有共享一个公共父级的组件,并记录它们属于哪个资产,例如:

--Make a recursive list of components sharing a common parent
WITH cte AS (
    SELECT
        Id,
        ParentComponentId,
        AssetId
    FROM
        @astComponents
    WHERE
        ParentComponentId IS NULL
    UNION ALL
    SELECT
        c.Id,
        c.ParentComponentId,
        cte.AssetId
    FROM
        @astComponents c
        INNER JOIN cte ON cte.Id = c.ParentComponentId)
SELECT * FROM cte;

我打赌你的桌子不是这样工作的,所以你需要调整它以适应。就我而言,我得到以下结果:

Id  ParentComponentId   AssetId
1       NULL        1
5       NULL        2
6       5           2
2       1           1
4       1           1
3       2           1

所以我可以看到组件 #1 和 #5 没有父组件,所以这些是 "top level" 个组件,我还可以看到哪些组件 link 属于哪些资产。

现在只需将查询的 SELECT * FROM cte 部分替换为如下内容即可:

SELECT 
    cte.Id,
    c.Code,
    c.ParentComponentId
FROM 
    cte
    INNER JOIN @astComponents c ON c.Id = cte.Id
    INNER JOIN @astAssets a ON a.Id = cte.AssetId
WHERE
    a.Code = '2018100000';

给我共享一个父项的所有组件,其中父项的资产代码为 201810000:

Id  Code    ParentComponentId
1   Power Supply    NULL
2   Fan 1
4   Heat Tape   1
3   Screw   2