【问题标题】:SQL Server Repeat first row for each node and get same row number for hierarchy nodeSQL Server 为每个节点重复第一行并为层次结构节点获取相同的行号
【发布时间】:2021-06-15 23:43:15
【问题描述】:

我有多个返回此结果的嵌套 cte

WITH X AS (    
  SELECT BATCH, CHILD_BATCH, 0 AS LVL, 
  N'/' + CONVERT(NVARCHAR(4000),BATCH) + N'/' + CONVERT(NVARCHAR(4000),CHILD_BATCH) + N'/' AS HIERARCHY
  FROM MYTABLE
  WHERE BATCH = @LOTTO_INIZIALE  

  UNION ALL

  SELECT T.BATCH, T.CHILD_BATCH, X.LVL + 1 AS LVL, X.HIERARCHY + CONVERT(NVARCHAR(4000), L.CHILD_BATCH) + N'/'
  FROM MYTABLE T INNER JOIN X ON T.BATCH = X.CHILD_BATCH  
)
SELECT * FROM X

╔════════╦═════════════╦═════════════════════════════════════════════╦═════╗
║ BATCH  ║ CHILD_BATCH ║                  HIERARCHY                  ║ LVL ║
╠════════╬═════════════╬═════════════════════════════════════════════╬═════╣
║ NZ1677 ║ NZ1671      ║ /NZ1677/NZ1671/                             ║   0 ║
║ NZ1671 ║ NZ1646      ║ /NZ1677/NZ1671/NZ1646/                      ║   1 ║
║ NZ1646 ║ NZ1125      ║ /NZ1677/NZ1671/NZ1646/NZ1125/               ║   2 ║
║ NZ1125 ║ NZ0960      ║ /NZ1677/NZ1671/NZ1646/NZ1125/NZ0960/        ║   3 ║
║ NZ0960 ║ NY2443      ║ /NZ1677/NZ1671/NZ1646/NZ1125/NZ0960/NY2443/ ║   4 ║
║ NZ1677 ║ NZ1672      ║ /NZ1677/NZ1672/                             ║   0 ║
║ NZ1672 ║ NZ1646      ║ /NZ1677/NZ1672/NZ1646/                      ║   1 ║
║ NZ1646 ║ NZ1125      ║ /NZ1677/NZ1672/NZ1646/NZ1125/               ║   2 ║
║ NZ1125 ║ NZ0960      ║ /NZ1677/NZ1672/NZ1646/NZ1125/NZ0960/        ║   3 ║
║ NZ0960 ║ NY2443      ║ /NZ1677/NZ1672/NZ1646/NZ1125/NZ0960/NY2443/ ║   4 ║
║ NZ1672 ║ NZ1647      ║ /NZ1677/NZ1672/NZ1647/                      ║   1 ║
║ NZ1647 ║ NZ1444      ║ /NZ1677/NZ1672/NZ1647/NZ1444/               ║   2 ║
║ NZ1444 ║ NZ1442      ║ /NZ1677/NZ1672/NZ1647/NZ1444/NZ1442/        ║   3 ║
║ NZ1442 ║ NY2443      ║ /NZ1677/NZ1672/NZ1647/NZ1444/NZ1442/NY2443/ ║   4 ║
╚════════╩═════════════╩═════════════════════════════════════════════╩═════╝

我希望为同一个层次结构节点获得一个具有相同值的新列,如下所示:

╔════════╦═════════════╦═════════════════════════════════════════════╦═════╦═════╗
║ BATCH  ║ CHILD_BATCH ║                  HIERARCHY                  ║ LVL ║ IX  ║
╠════════╬═════════════╬═════════════════════════════════════════════╬═════╬═════╣
║ NZ1677 ║ NZ1671      ║ /NZ1677/NZ1671/                             ║   0 ║   1 ║
║ NZ1671 ║ NZ1646      ║ /NZ1677/NZ1671/NZ1646/                      ║   1 ║   1 ║
║ NZ1646 ║ NZ1125      ║ /NZ1677/NZ1671/NZ1646/NZ1125/               ║   2 ║   1 ║
║ NZ1125 ║ NZ0960      ║ /NZ1677/NZ1671/NZ1646/NZ1125/NZ0960/        ║   3 ║   1 ║
║ NZ0960 ║ NY2443      ║ /NZ1677/NZ1671/NZ1646/NZ1125/NZ0960/NY2443/ ║   4 ║   1 ║
║ NZ1677 ║ NZ1672      ║ /NZ1677/NZ1672/                             ║   0 ║   2 ║
║ NZ1672 ║ NZ1646      ║ /NZ1677/NZ1672/NZ1646/                      ║   1 ║   2 ║
║ NZ1646 ║ NZ1125      ║ /NZ1677/NZ1672/NZ1646/NZ1125/               ║   2 ║   2 ║
║ NZ1125 ║ NZ0960      ║ /NZ1677/NZ1672/NZ1646/NZ1125/NZ0960/        ║   3 ║   2 ║
║ NZ0960 ║ NY2443      ║ /NZ1677/NZ1672/NZ1646/NZ1125/NZ0960/NY2443/ ║   4 ║   2 ║
║ NZ1672 ║ NZ1647      ║ /NZ1677/NZ1672/NZ1647/                      ║   1 ║   3 ║
║ NZ1647 ║ NZ1444      ║ /NZ1677/NZ1672/NZ1647/NZ1444/               ║   2 ║   3 ║
║ NZ1444 ║ NZ1442      ║ /NZ1677/NZ1672/NZ1647/NZ1444/NZ1442/        ║   3 ║   3 ║
║ NZ1442 ║ NY2443      ║ /NZ1677/NZ1672/NZ1647/NZ1444/NZ1442/NY2443/ ║   4 ║   3 ║
╚════════╩═════════════╩═════════════════════════════════════════════╩═════╩═════╝

有可能吗?

如果有多个分层子节点,我还希望重复级别 0。 (例如/NZ1677/NZ1672/)

编辑

解释起来有点复杂,但我试试。

在我的表中,我有一个包含一个或多个孩子的批次列表。

对于每个层次结构级别,我可以有一个 o 加子级别。

在每一行中,我都有批次和子项的数量。

例如,另一个批次可以是这样的(如果有帮助,我还添加了“prev_batch”列):

+------------+--------+-------------+-----------+-----------+------------------------+-----+
| PREV_BATCH | BATCH  | CHILD_BATCH | BATCH_QTY | CHILD_QTY |       HIERARCHY        | LVL |
+------------+--------+-------------+-----------+-----------+------------------------+-----+
|            | NB0166 | NA1367M     |  119.3700 |    0.0450 | /NB0166/NA1367M/       |   0 |
|            | NB0166 | NA1938M     |  119.3700 |    0.0650 | /NB0166/NA1938M/       |   0 |
|            | NB0166 | NA3204S     |  119.3700 |    0.0270 | /NB0166/NA3204S/       |   0 |
|            | NB0166 | NB0019      |  119.3700 |   18.0400 | /NB0166/NB0019/        |   0 |
| NB0166     | NB0019 | NA3938      |  109.6700 |  100.0000 | /NB0166/NB0019/NA3938/ |   1 |
|            | NB0166 | NB0021      |  119.3700 |   91.9600 | /NB0166/NB0021/        |   0 |
| NB0166     | NB0021 | NA1924      |  109.7400 |   40.0000 | /NB0166/NB0021/NA1924/ |   1 |
| NB0166     | NB0021 | NA2251      |  109.7400 |   10.0000 | /NB0166/NB0021/NA2251/ |   1 |
| NB0166     | NB0021 | NA2616      |  109.7400 |   50.0000 | /NB0166/NB0021/NA2616/ |   1 |
+------------+--------+-------------+-----------+-----------+------------------------+-----+

我的想法是为每个层次结构节点添加具有相同值的 IX 列(就像我在上面解释的那样)。

我想用lead函数读取下一级批次的数量(我需要做一些计算)。

使用 IX 列,我可以正确划分潜在客户。

对完成这项任务的最佳想法持开放态度。

【问题讨论】:

  • 旁白:;WITH 终止了什么?为什么不是声明terminated
  • 帖子已编辑以澄清
  • 我会假设 BATCH_QTY = SUM(CHILD_QTY) 但这不是您的数据所暗示的。您能否阐明数据中的数量列之间的关系?
  • 批次数量不是孩子的总和,因为它是一个产品从自己的原材料中生产出来的数量。推理也反映在每个子批次中。

标签: sql sql-server tsql common-table-expression hierarchical-data


【解决方案1】:

如果我理解正确,当上一行比当前行更深时,您希望增加。

这可能是使用LAG() 和累积总和最简单的方法:

WITH X AS (    
  SELECT BATCH, CHILD_BATCH, 0 AS LVL, 
         (N'/' + CONVERT(NVARCHAR(4000),BATCH) + N'/' + 
          CONVERT(NVARCHAR(4000),CHILD_BATCH) + N'/'
         ) AS HIERARCHY
  FROM MYTABLE
  WHERE BATCH = @LOTTO_INIZIALE  
  UNION ALL
  SELECT T.BATCH, T.CHILD_BATCH, X.LVL + 1 AS LVL,
         (X.HIERARCHY + CONVERT(NVARCHAR(4000), L.CHILD_BATCH) + N'/')
  FROM MYTABLE T INNER JOIN
       X
       ON T.BATCH = X.CHILD_BATCH  
)
SELECT X.*,
       SUM(CASE WHEN prev_lvl < lvl THEN 0 ELSE 1 END) OVER (ORDER BY HIERARCHY) as IX
FROM (SELECT X.*,
             LAG(LVL) OVER (ORDER BY HIERARCHY) as prev_lvl
      FROM X
     ) X;

【讨论】:

    【解决方案2】:

    在您的情况下可能无关紧要,但我建议该记录具有“父”列而不是“子”列。这是因为在许多情况下,当您插入具有父/子关系的记录时,“父”已经存在,而子可能要等到以后才能插入。这意味着当您插入记录时,您手头有您需要的所有信息,而不必在插入子项后去更新行。此外,孩子通常只有一个父母,但父母记录可以有任意数量的孩子。您还可以更轻松地实施参照完整性。

    不管你的问题。

    我将以下内容解释为您要求每个“组”都有一个“0”记录。这就是为什么我的输出中有一个额外的“0”记录,而你的输出中没有。

    如果有多个层次子节点,我也希望重复第 0 级。

    鉴于 IX 只是在“孙子”上创建组的一种方式,我想知道是否可能需要在任何可用的后代级别上进行分组,因此我已将其参数化。我没有足够的数据差异来测试这个,但它看起来应该可以工作。

    DECLARE @MYTABLE TABLE
    (   BATCH CHAR(6),
        CHILD_BATCH CHAR(6)
    )
    
    DECLARE @LOTTO_INIZIALE CHAR(6) = 'NZ1677'
    DECLARE @GROUPING_LEVEL INT     = 1 -- 1 = grandchild, 2 = great-grandchild... n = nth descendant +2 (given our base is child)
    
    INSERT INTO @MYTABLE
    VALUES ('NZ1677', 'NZ1671'),
           ('NZ1671', 'NZ1646'),
           ('NZ1646', 'NZ1125'),
           ('NZ1125', 'NZ0960'),
           ('NZ0960', 'NY2443'),
           ('NZ1677', 'NZ1672'),
           ('NZ1672', 'NZ1646'),
           ('NZ1646', 'NZ1125'),
           ('NZ1125', 'NZ0960'),
           ('NZ0960', 'NY2443'),
           ('NZ1672', 'NZ1647'),
           ('NZ1647', 'NZ1444'),
           ('NZ1444', 'NZ1442'),
           ('NZ1442', 'NY2443')
    
    ;WITH cte_batch_hierarchy
       AS (SELECT BATCH, 
                  CHILD_BATCH,
                  GRP              = CONVERT(VARCHAR(MAX), '/') + BATCH + '/' + CHILD_BATCH + '/',
                  HIERARCHY        = CONVERT(VARCHAR(MAX), '/') + BATCH + '/' + CHILD_BATCH + '/',
                  LVL              = 0
             FROM @MYTABLE  t
            WHERE t.BATCH = @LOTTO_INIZIALE 
            UNION ALL
           SELECT mt.BATCH, 
                  mt.CHILD_BATCH,
                  GRP = CASE WHEN bh.LVL < @GROUPING_LEVEL THEN HIERARCHY + mt.CHILD_BATCH  + '/' ELSE GRP END,
                  HIERARCHY = HIERARCHY + mt.CHILD_BATCH  + '/',
                  LVL       = LVL + 1
             FROM cte_batch_hierarchy bh
             JOIN @MYTABLE mt ON bh.CHILD_BATCH = mt.BATCH
          )
    SELECT bh.BATCH, 
           bh.CHILD_BATCH, 
           bh.HIERARCHY, 
           bh.LVL, 
           IX = DENSE_RANK() OVER(ORDER BY ISNULL(grp.GRP, bh.GRP)) 
      FROM cte_batch_hierarchy bh
      LEFT JOIN (SELECT DISTINCT GRP 
                   FROM cte_batch_hierarchy 
                  WHERE LVL > @GROUPING_LEVEL) grp ON grp.GRP LIKE bh.HIERARCHY + '%'
     GROUP BY
           bh.BATCH, 
           bh.CHILD_BATCH, 
           bh.HIERARCHY, 
           bh.LVL,
           ISNULL(grp.GRP, bh.GRP)
     ORDER BY 
           DENSE_RANK() OVER(ORDER BY ISNULL(grp.GRP, bh.GRP)),
           LVL
    

    【讨论】:

    • 谢谢,它适用于给定的示例,但是如果我从一开始就不知道每个节点可以拥有的子节点数量,我该怎么办?
    • 为了完成信息,我需要使用IX字段才能使用引导功能分区仅用于降层
    • 我不明白您还想从这些 cmets 中寻找什么。你能用额外的要求更新问题吗?有更多的空间可以清除。
    • 你说得对。我已经编辑了帖子,希望现在更清楚了。
    猜你喜欢
    • 1970-01-01
    • 2019-11-19
    • 2015-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多