Возврат всех компонентов нескольких уровней под родительским компонентом - PullRequest
1 голос
/ 09 мая 2019

У меня есть иерархическая база данных, из которой я хотел бы вернуть все компоненты в активе с специфическим общим родительским компонентом.

Компоненты в таблице astComponents связаны с их родительскими компонентами с ParentComponentId, относящимся к Id других компонентов. Это, однако, показывает только родительский уровень на один уровень выше.

Кроме того, все компоненты связаны с активом с помощью ключа AssetId, связанного с ключом Id в таблице astAssets.

Я могу сделать простой запрос (ниже), чтобы получить все компоненты из определенного ресурса. Я не могу понять, как вернуть все компоненты из определенного ресурса, совместно использующего один и тот же родительский компонент. Этот родительский компонент может (относительно дочернего компонента) от 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'

Приведенный выше запрос возвращает компоненты и их родителей на один уровень выше. Если бы я указал родительский код, он вернул бы дочерние компоненты только на один уровень ниже указанного компонента, а не все уровни вниз.

Ответы [ 2 ]

1 голос
/ 09 мая 2019

Хорошо, без каких-либо данных для работы здесь происходит много догадок. Я создал некоторые данные:

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 не имеют родителей, поэтому это компоненты «верхнего уровня», и я также могу видеть, какие компоненты связаны с какими активами.

Теперь это просто случай замены 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
0 голосов
/ 09 мая 2019

Итак, если я правильно понял, у вас есть известный родительский компонент, и известный код актива. Используя их, вы хотите построить дерево, охватывающее пути (из-за отсутствия лучшего слова) от родительского компонента к рассматриваемому активу?

Вот попытка сделать это. Мы начнем с создания фиктивных данных, которые будут использоваться для тестирования:

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

В этом случае мы ищем актив 2018100000 под компонентом с идентификатором 3. Результат:

| 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 начинается с поиска всех компонентов, связанных с известным активом. Затем он рекурсивно добавляет все родительские узлы на основе этого.

Наконец, мы запрашиваем комбинацию этих двух, чтобы получить окончательный набор результатов.

...