Как представить иерархические данные в SQL Server 2014 - PullRequest
2 голосов
/ 04 марта 2020

У меня есть две таблицы Company и CompanyRelationShip.

DECLARE @Company TABLE (
    CompanyId INT
    ,RootCompanyId INT
    ,CompanyName VARCHAR(100)
    )

INSERT INTO @Company
VALUES (2,2,'ROOT')
,(106,2,'ABC')
,(105,2,'CDF')
,(3,3,'ROOT2')
,(150,3,'YXZ')
,(151,3,'XZX')


DECLARE @CompanyRelationShip TABLE (
    PrimaryCompanyId INT
    ,CompanyId INT
    )

INSERT INTO @CompanyRelationShip
VALUES (2,2)
,(2,106)
,(2,105)
,(106,105)
,(3,3)
,(3,151)
,(3,150)
,(151,150)

Я хочу получить результат в следующем формате

CompanyId   PrimayCompanyId PrimaryCompanyName  RootCompanyId   RootCompanyName
2                 2              ROOT               2               ROOT
106               2              ROOT               2               ROOT
105              106             ABC                2               ROOT
3                 3              ROOT2              3               ROOT2
151              3               ROOT2              3               ROOT2
150              151             XZX                3               ROOT2

Я пробовал приведенный ниже запрос, чтобы получить результат

WITH PrimayCompany
AS (
    SELECT CR.PrimaryCompanyId
        ,C.CompanyName
    FROM @CompanyRelationShip CR
    JOIN @Company C ON CR.CompanyId = CR.PrimaryCompanyId
    )
    ,RootCompany
AS (
    SELECT RootCompanyId
        ,CompanyName
    FROM @Company
    WHERE CompanyId = RootCompanyId
    )
SELECT C.CompanyId
    ,C.RootCompanyId
    ,RC.CompanyName
    ,CR.PrimaryCompanyId
    ,PC.CompanyName
FROM @Company C
LEFT JOIN @CompanyRelationShip CR ON C.CompanyId = CR.PrimaryCompanyId
LEFT JOIN PrimayCompany PC ON PC.PrimaryCompanyId = CR.PrimaryCompanyId
LEFT JOIN RootCompany RC ON RC.RootCompanyId = CR.PrimaryCompanyId

Буду очень признателен за помощь.

1 Ответ

1 голос
/ 04 марта 2020

В своем комментарии я спросил вас, зачем вам вообще нужна таблица @CompanyRelationShip ... Это просто добавляет чертовски много сложности и возможностей для ошибок.

Мое предложение основано на первый стол один. Посмотрите, как я изменил родительские идентификаторы 105 и 151, чтобы поместить их ниже в иерархии. Просто чтобы показать принципы, я добавил второго ребенка ниже 150 :

DECLARE @Company TABLE (
    CompanyId INT
    ,RootCompanyId INT
    ,CompanyName VARCHAR(100)
    );

INSERT INTO @Company
VALUES (2,2,'ROOT')
,(106,2,'ABC')
,(105,106,'CDF')
,(3,3,'ROOT2')
,(150,3,'YXZ')
,(151,150,'XZX')
,(152,150,'Second below 150');

- запрос

WITH recCTE AS
(
    SELECT CompanyId AS [RootId],CompanyName AS [RootName],*,1 AS HierarchyLevel FROM @Company WHERE CompanyId=RootCompanyId
    UNION ALL
    SELECT rc.RootId,rc.RootName,c.*,rc.HierarchyLevel+1
    FROM @Company c
    INNER JOIN recCTE rc ON c.RootCompanyId=rc.CompanyId AND c.CompanyId<>rc.CompanyId
)
SELECT RootId
      ,RootName
      ,RootCompanyId AS [PrevId]
      ,CompanyId
      ,CompanyName
      ,HierarchyLevel 
FROM recCTE rc
ORDER BY RootId,HierarchyLevel;

Результат

RootId  RootName    PrevId  CompanyId   CompanyName     HierarchyLevel
2       ROOT        2       2           ROOT                1
2       ROOT        2       106         ABC                 2
2       ROOT        106     105         CDF                 3
3       ROOT2       3       3           ROOT2               1
3       ROOT2       3       150         YXZ                 2
3       ROOT2       150     151         XZX                 3
3       ROOT2       150     152         Second below 150    3

Идея вкратце:

  • Мы используем рекурсивный CTE (на самом деле это довольно итеративная концепция).
  • Первый SELECT ( якорь ) начинается с компаний, в которых совпадают два идентификатора.
  • Второй SELECT после UNION ALL выбирает следующий уровень, присоединяя к промежуточная строка результата
  • Два столбца RootId и RootName просто пропущены, чтобы отобразиться в вашем окончательном наборе.

HierarchyLevel - это позиция в пределах линия, таким образом помещая 105 в пределах ROOT, но ниже 106.

Надеюсь, это поможет ...

Решение для данной структуры

Как уже говорилось, данная структура не является лучшим выбором и должна быть изменена. Но если вам нужно придерживаться этого, вы можете попробовать кое-что по этому поводу:

WITH recCTE AS
(
    SELECT CompanyId AS [RootId],CompanyName AS [RootName],*,1 AS HierarchyLevel FROM @Company WHERE CompanyId=RootCompanyId
    UNION ALL
    SELECT rc.RootId,rc.RootName,c.*,rc.HierarchyLevel+1
    FROM @Company c
    INNER JOIN recCTE rc ON c.RootCompanyId=rc.CompanyId AND c.CompanyId<>rc.CompanyId
)
SELECT rc.CompanyId
      ,rc.CompanyName  
      ,COALESCE(crs.PrimaryCompanyId,rc.RootCompanyId) AS ComputedPrevId
      ,COALESCE(c1.CompanyName,rc.RootName) AS ComputedPrevName
      ,rc.RootId
      ,rc.RootName
FROM recCTE rc
LEFT JOIN @CompanyRelationShip  crs ON rc.CompanyId=crs.CompanyId AND rc.RootCompanyId<>crs.PrimaryCompanyId
LEFT JOIN @Company c1 ON crs.PrimaryCompanyId=c1.CompanyId
ORDER BY rc.RootId,rc.HierarchyLevel;

Сначала будет использован рекурсивный CTE, чтобы найти детей ниже их root компаний, и он попытается найти соответствующий строка в таблице ваших отношений.

Если вы используете просто SELECT * вместо списка столбцов, вы можете увидеть полный набор. Использование LEFT JOIN вернет NULL, когда условие ON не выполнено.

COALESCE вернет первое значение, отличное от NULL, так что - надеюсь - то, что вы ищете.

...