【问题标题】:Updating a Parent Record's field using Child's Record Data (Same Table)使用子记录数据(同一表)更新父记录字段
【发布时间】:2016-12-07 00:22:21
【问题描述】:

我有一个表,其中存在父/子关系,其中每个孩子的ExternalCategoryCode 列数据需要连接到父母ExternalCategoryCode

这是一些损坏的示例数据,其中父记录 (Id=96) ExternalCategoryCodeNULL。关系是通过ParentId 字段:

Id |Name                          |ExternalCategoryCode|ParentId
---|------------------------------|--------------------|--------- 
96 | Health & Personal Development| NULL               | NULL
---|------------------------------|--------------------|---------
97 | Health                       | H1*;H2*            | 96
---|------------------------------|--------------------|---------
98 | Personal Development         | P1;P2;P3*          | 96
---|------------------------------|--------------------|---------
99 | Other Health                 | OH*                | 96
---|------------------------------|--------------------|---------

数据应如下所示:

Id |Name                          |ExternalCategoryCode  |ParentId
---|------------------------------|----------------------|--------- 
96 | Health & Personal Development| H1*;H2*;P1;P2;P3*;OH*| NULL
---|------------------------------|----------------------|---------
97 | Health                       | H1*;H2*              | 96
---|------------------------------|----------------------|---------
98 | Personal Development         | P1;P2;P3*            | 96
---|------------------------------|----------------------|---------
99 | Other Health                 | OH*                  | 96
---|------------------------------|----------------------|---------

编辑:

  1. 后代层可以是任何深度,但深度子级 后代需要更新到主要父母的 ExternalCategoryCode。取下面的最后两条记录 (Id=100Id=101) 都链接到 ParentId 97。因为 Id=97 ParentId IS NOT NULL,我们应该继续向上树,直到我们 查找ParentId 为 NULL 的记录(最高父级)。
  2. 另一个问题是关于最高父级的ExternalCategoryCode 是否包含重复项?下面的示例显示了 M2* 两次。这很好,因为我们的业务逻辑类库会在找到重复项时过滤掉。
  3. 如果记录在其ParentId 中有链接(即是子项) 有一个 NULL ExternalCategoryCode,则可以忽略这些代码。

    Id |Name                          |ExternalCategoryCode  |ParentId
    ---|------------------------------|----------------------|--------- 
    96 | Health & Personal Development| H1*;H2*;P1;P2;P3*;OH*| NULL
       |                              |                      |
       |                              | M1;M2*; M2*;M3*      |
    ---|------------------------------|----------------------|---------
    97 | Health                       | H1*;H2*              | 96
    ---|------------------------------|----------------------|---------
    98 | Personal Development         | P1;P2;P3*            | 96
    ---|------------------------------|----------------------|---------
    99 | Other Health                 | OH*                  | 96
    ---|------------------------------|----------------------|---------
    100| Medicine                     | M1;M2*               | 97
    ---|------------------------------|----------------------|---------
    101| Other Medicine               | M2*;M3*              | 97
    ---|------------------------------|----------------------|---------
    102| Other Medicine 2             | NULL                 | 97
    ---|------------------------------|----------------------|---------
    

表中还有其他父/子损坏。我该怎么做:

  1. 编写此脚本以记录有问题的表并连接 父母记录ExternalCategoryCode与孩子 ExternalCategoryCode数据?

  2. 列出更新的父记录。通常在使用UPDATED 时,这只是显示这样的内容,我想报告这个:

    (受影响的 1 行)

我正在使用的技术:

  • SQL 服务器

【问题讨论】:

  • 后代层有多深?例如,97 是 96 的子代。那么会有一个 101 是 97 的子代吗?另外,会不会有两个child的code相同的情况(比如H1),那么parent中的code是否需要减少这个重复?
  • @DVT 我将用这些答案编辑问题。
  • 已添加编辑帖子。
  • 所以你只想更新最顶层的父级,而不是中间级别的?
  • 没错 - 我们使用最顶层的父级作为“包罗万象”的分组目的。无论出于何种原因,我发现我们的一些父母没有所有的孩子(以及他们的孩子等)代码。

标签: sql sql-server parent-child


【解决方案1】:

SQL DEMO 首先连接来自同一父级的所有代码,然后更新。

WITH superParent as (
    SELECT [Id], [Id] as [topParent], [Name], [ExternalCategoryCode], [ParentId]
    FROM Table1 t
    WHERE [ParentId] is NULL
    UNION ALL
    SELECT t.[Id], sp.[topParent], t.[Name], t.[ExternalCategoryCode], t.[ParentId]    
    FROM Table1 t
    JOIN superParent sp
      ON sp.[id] = t.[ParentId]
),  
combine as (
    Select distinct ST2.[topParent], 
        (
            Select ST1.[ExternalCategoryCode] + ';' AS [text()]
            From superParent ST1
            Where ST1.[topParent] = ST2.[topParent]
            ORDER BY ST1.[Id]
            For XML PATH ('')
        ) [External]
    From superParent ST2
    WHERE ST2.[ParentId] IS NOT NULL    
)    
UPDATE T
SET T.[ExternalCategoryCode] = C.[External]
FROM Table1 T
JOIN combine C
  ON T.[Id] = c.[topParent];

SELECT *
FROM Table1;

OUTPUT 使用递归 cte 将最高父级分配给每个子级。然后使用 XML PATH 连接所有CategoryCode

【讨论】:

  • 最后一条记录的 ID 是 98 而在 OP 中应该是 99
  • @GuidoG 谢谢,不知道如果我只是复制/粘贴会发生什么:$
  • @JuanCarlosOropeza:我在发布并快速编辑后注意到了这一点。只是显示你有多快! :)
  • @garfbradaz 哦,有道理,
  • 嘿伙计们,抱歉让我沉默了,我明天早上去看看这个并报告。感谢您花时间看这个。
【解决方案2】:

这个很难。试试这个。

CREATE TABLE #Table1
    ([Id] int, [Name] varchar(29), [ExternalCategoryCode] nvarchar(max), [ParentId] varchar(4))
;

INSERT INTO #Table1
    ([Id], [Name], [ExternalCategoryCode], [ParentId])
VALUES
    (96, 'Health & Personal Development', NULL, NULL),    
    (97, 'Health', 'H1*;H2*', '96'),
    (98, 'Personal Development', 'P1;P2;P3*', '96'),
    (99, 'Other Health', 'OH*', '96'),
    (100, 'Medicine', 'M1;M2*', '97'),
    (101, 'Other Medicine', 'M2*;M3*', '97'),
    (102, 'Other Medicine 2', NULL, '97')
;

WITH cte AS (
    SELECT
        Id
        , ParentId AS Direct_Parent
        , Id AS Orig_Parent
        , ExternalCategoryCode
    FROM
        #Table1
    WHERE
        ParentId IS NULL

    UNION ALL

    SELECT
        t1.Id
        , t1.ParentId
        , cte.Orig_Parent
        , t1.ExternalCategoryCode
    FROM
        #Table1 t1
        JOIN cte ON t1.ParentId = cte.Id
), tmp1 AS (
    SELECT DISTINCT
        Orig_Parent
        , ExternalCategoryCode
    FROM
        cte
    WHERE
        Id <> Orig_Parent
        AND ExternalCategoryCode IS NOT NULL
), tmp2 AS (  -- If there are too many children, this one might be needed, other wise, just use tmp1 in tmp3 below
    SELECT DISTINCT
        Orig_parent
    FROM
        tmp1
), tmp3 AS (
    SELECT
        tmp2.Orig_Parent,
        (
        SELECT CONCAT(tmp1.ExternalCategoryCode,',')
        FROM
            tmp1
        WHERE
            tmp1.Orig_parent = tmp2.Orig_Parent
        FOR XML PATH(''),type).value('.','NVARCHAR(MAX)')
        AS New_string 
    FROM
        tmp2
)
UPDATE tab
SET ExternalCategoryCode = tmp3.New_string
FROM #Table1 tab JOIN tmp3 ON tab.Id = tmp3.Orig_Parent

SELECT *
FROM
    #Table1;


DROP TABLE #Table1;

【讨论】:

  • 更新了代码。它现在应该运行。谢谢@JuanCarlosOropeza
  • 发布rextester?顺便说一句...CONCAT(CONCAT('-',t1.Id),'-')) 等于CONCAT('-', t1.Id ,'-' )
  • 没有。我从那里复制了你的测试代码。我以前从未使用过rextester。感谢您提供有关 CONCAT 的提示。
  • 只需按Save 即可获取新链接。
  • 不知道control_string 有什么用?我的版本没有使用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-31
  • 2013-01-28
  • 1970-01-01
  • 2014-07-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多