【问题标题】:TSQL - Mapping Parent/Child HierarchySQL - 映射父/子层次结构
【发布时间】:2020-08-28 23:33:00
【问题描述】:

样本数据:

DECLARE @Hierarchy TABLE
    (
        [ParentId] INT
      , [ChildId]  INT
    ) ;

INSERT INTO @Hierarchy
VALUES
    ( 1, 2 )
  , ( 1, 3 )
  , ( 2, 4 )
  , ( 3, 5 )
  , ( 4, 3 )
  , ( 4, 6 )
  , ( 5, 6 )
  , ( 7, 3 ) ;

当前查询:

; WITH CTE AS
    (
        SELECT  [ParentId]
              , [ChildId]
              , 1 AS [Level]
              , CONCAT ( CAST ( [ParentId] AS VARCHAR(MAX) ), '.', CAST ( [ChildId] AS VARCHAR(MAX) ) )  AS [Path]
        FROM    @Hierarchy
        UNION ALL
        SELECT  [C].[ParentId]
              , [T].[ChildId]
              , [C].[Level] + 1
              , CAST ( [C].[Path] + '.' + CAST([T].[ChildId] AS VARCHAR(MAX) ) AS VARCHAR(MAX) )
        FROM    CTE AS [C]
        JOIN    @Hierarchy AS [T]
        ON      [C].[ChildId] = [T].[ParentId]
    )
SELECT      *
FROM        CTE
ORDER BY    [ParentId]
          , [Level]
          , [ChildId] ;

目标:

  1. 将共享“路径”的级别明确分组在一起
  2. 找到共享“路径”的最浅层和最深层

预期输出:

注意:最后带有橙色突出显示的记录是手动插入的,以显示我所期待的,但还没有弄清楚。

组:基本上是遵循相同路径的每个“组”节点的“密集等级”。我认为如果您查看上图中 Group 的值并将其与 Level 和 Path 字段的数据相关联,它会更有意义。

IsShallowest:1 级(我现在可以看到有人提出了它)。只需要弄清楚如何导出那些丢失(重复)的记录

IsDeepest:组内的最高级别。

一旦弄清楚“组”逻辑并添加缺失(重复)的记录,就很容易弄清楚 IsShallowest 和 IsDeepest。

【问题讨论】:

  • 什么是“组”?
  • ;有趣的是,IsShallowest 几乎但不总是与Level = 1 相关。 ;我想知道是什么让1.2.4.6 最浅。 ;
  • ChildId 3如何得到三个ParentId(1、4、7)?
  • @AlwaysLearning,据我所知,描述不佳,这不是树层次结构,而是多对多结构。您可以注意到 chaildid 3 在源表中有这三个 ParentId (1, 4, 7)。这些数据似乎最适合图形建模而不是关系建模。如果在 SQL Server 2019 中使用图形功能重新设计数据库,它可能会更好地工作。
  • @007, (1) 请说明您在预期结果中手动插入的行的含义以及如何从源数据中得出该行。我现在发现这一行没有逻辑。为什么要添加这一行?!? (2) 请解释组背后的逻辑 - 逐行解释你如何以及为什么将这个特定值作为组值

标签: sql sql-server tsql sql-server-2016 hierarchy


【解决方案1】:

请检查此解决方案。它提供了所需的解决方案,除了添加需要更多信息的额外行

;WITH CTE AS (
    SELECT  
        [ParentId]
        , [ChildId]
        , 1 AS [Level]
        , CONCAT ( CAST ( [ParentId] AS VARCHAR(MAX) ), '.', CAST ( [ChildId] AS VARCHAR(MAX) ) )  AS [Path]
        , MyGroup1 = ROW_NUMBER() OVER(ORDER BY [ParentId])
        , MyGroup2 = ROW_NUMBER() OVER(ORDER BY [ParentId])
    FROM Hierarchy
    UNION ALL
    SELECT  
        [C].[ParentId]
        , [T].[ChildId]
        , [C].[Level] + 1
        , CAST ( [C].[Path] + '.' + CAST([T].[ChildId] AS VARCHAR(MAX) ) AS VARCHAR(MAX) )
        , MyGroup1 = C.MyGroup1
        , MyGroup2 = [C].[MyGroup1] + ROW_NUMBER() OVER(ORDER BY [T].[ParentId]) - 1
    FROM CTE AS [C]
    JOIN Hierarchy AS [T] ON [C].[ChildId] = [T].[ParentId]
)
, MyCTE2 as (
    SELECT
        [ParentId]
        , [ChildId]
        , [Level]
        , [Path]
        -- un-comment bellow 2 rows to see the logic
        --, MyGroup1
        --, MyGroup2
        , MyGroup = DENSE_RANK() OVER (ORDER BY MyGroup1, MyGroup2)
    FROM CTE
),
MyCTE3 as (
    SELECT
        [ParentId]
        , [ChildId]
        , [Level]
        , [Path]
        , MyGroup 
        , shallowest = ROW_NUMBER() OVER(PARTITION BY MyGroup ORDER BY [Path]) 
        , deepest = ROW_NUMBER() OVER(PARTITION BY MyGroup ORDER BY [Path] DESC) 
    FROM MyCTE2
)
SELECT
    [ParentId]
    , [ChildId]
    , [Level]
    , [Path]
    , MyGroup 
    , ISshallowest = CASE WHEN shallowest = 1 then 1 else 0 END
    , Isdeepest = CASE WHEN deepest = 1 then 1 else 0 END
FROM MyCTE3
ORDER BY
    --[Path]
    [ParentId]
    , [Level]
    , [ChildId];

【讨论】:

  • 这太棒了!!谢谢你的帮助,真的很感激。我需要一些时间来弄清楚代码中发生了什么,以便我可以将它合并到我的生产场景中,但输出看起来很重要。至于手动插入的记录,我已经更新了 OP 并在那里添加了评论。请让我知道它是否仍然令人困惑。干杯伙伴!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-23
  • 2020-05-29
相关资源
最近更新 更多