首页 > 解决方案 > 如何在 SQL Server 2014 中呈现分层数据

问题描述

我有两张桌子CompanyCompanyRelationShip.

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

我真的很感激一些帮助。

标签: sql-servertsqlsql-server-2014

解决方案


在我的评论中,我问你,为什么你需要这张桌子@CompanyRelationShip......这只是增加了很多复杂性和潜在的错误。

我的建议仅依赖于第一张桌子。看,我如何更改 105 和 151 的父 ID 以将它们放在层次结构的下方。只是为了展示我在 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(锚点)从两个 ID 匹配的公司开始。
  • UNION ALL 之后的第二个 SELECT 通过加入中间结果行来选择下一个级别
  • 这两列RootIdRootName刚刚通过以显示在您的最后一组中。

是行内的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 查找其根公司下的子代,然后尝试在您的关系表中找到相应的行。

如果您只使用SELECT *而不是列列表,您可以看到完整的集合。当不满足条款时,使用LEFT JOIN将返回。NULLON

COALESCE将返回第一个非 NULL 值,所以 - 希望 - 你之后的那个。


推荐阅读