6

我有一种情况,我需要配置现有的客户端数据来解决我们的应用程序没有正确更新表中的 ID 而应该正确更新的问题。

这是场景。我们有一个父表,可以在其中插入有效替换现有行的行;替换可以是递归的。我们还有一个子表,它有一个指向父表的字段。在现有数据中,子表可能指向已被替换的行,我需要更正它。但是,我不能简单地将每一行更新为替换行,因为行也可能已被替换,我需要反映最新的行。

我试图找到一种方法来编写一个可以为我完成此任务的 CTE,但我正在努力寻找一个能够找到我真正想要的东西的查询。这是我正在使用的表格的示例;'ShouldBe' 列是我希望我的更新查询最终得到的结果,同时考虑到某些行的递归替换。

DECLARE @parent TABLE (SampleID int, 
                   SampleIDReplace int,
                   GroupID char(1))

INSERT INTO @parent (SampleID, SampleIDReplace, GroupID)
VALUES (1, -1, 'A'), (2, 1, 'A'), (3, -1, 'A'), 
       (4, -1, 'A'), (5, 4, 'A'), (6, 5, 'A'),
       (7, -1, 'B'), (8, 7, 'B'), (9, 8, 'B')


DECLARE @child TABLE (ChildID int, ParentID int)
INSERT INTO @child (ChildID, ParentID)
VALUES (1, 4), (2, 7), (3, 1), (4, 3)

应用更新脚本后,子表中的所需结果:

ChildID     ParentID    ParentID_ShouldBe
1           4           6 (4 replaced by 5, 5 replaced by 6)
2           7           9 (7 replaced by 8, 8 replaced by 9)
3           1           2 (1 replaced by 2)
4           3           3 (unchanged, never replaced)
4

3 回答 3

4

以下返回您正在寻找的内容:

with cte as (
    select sampleid, sampleidreplace, 1 as num
    from @parent
    where sampleidreplace <> -1
    union all
    select p.sampleid, cte.sampleidreplace, cte.num+1
    from @parent p join
         cte
         on p.sampleidreplace = cte.sampleId
)
select c.*, coalesce(p.sampleid, c.parentid)
from @child c left outer join
     (select ROW_NUMBER() over (partition by sampleidreplace order by num desc) as seqnum, *
      from cte
     ) p
     on c.ParentID = p.SampleIDReplace and p.seqnum = 1

递归部分跟踪每个对应关系(4-->5、4-->6)。添加数是“世代”计数。我们实际上想要最后一代。这是通过使用row_number()函数来识别的,按 num 以降序排列 - 因此p.seqnum = 1.

于 2012-12-26T15:08:11.723 回答
2

好的,所以我花了一段时间,可能有更好的方法来做到这一点,但这里有一个选择。

DECLARE @parent TABLE (SampleID int, 
                   SampleIDReplace int,
                   GroupID char(1))

INSERT INTO @parent (SampleID, SampleIDReplace, GroupID)
VALUES (1, -1, 'A'), (2, 1, 'A'), (3, -1, 'A'), 
       (4, -1, 'A'), (5, 4, 'A'), (6, 5, 'A'),
       (7, -1, 'B'), (8, 7, 'B'), (9, 8, 'B')


DECLARE @child TABLE (ChildID int, ParentID int)
INSERT INTO @child (ChildID, ParentID)
VALUES (1, 4), (2, 7), (3, 1), (4, 3)


;WITH RecursiveParent1 AS
(
    SELECT SampleIDReplace, SampleID, 1 RecursionLevel
    FROM @parent
    WHERE SampleIDReplace != -1
    UNION ALL
    SELECT A.SampleIDReplace, B.SampleID, RecursionLevel + 1
    FROM RecursiveParent1 A
    INNER JOIN @parent B
        ON A.SampleId = B.SampleIDReplace
),RecursiveParent2 AS
(
    SELECT  *, 
            ROW_NUMBER() OVER(PARTITION BY SampleIdReplace ORDER BY RecursionLevel DESC) RN
    FROM RecursiveParent1
)
SELECT A.ChildID, ISNULL(B.ParentID,A.ParentID) ParentID
FROM @child A
LEFT JOIN ( SELECT SampleIDReplace, SampleID ParentID 
            FROM RecursiveParent2
            WHERE RN = 1) B
    ON A.ParentID = B.SampleIDReplace
OPTION(MAXRECURSION 500)
于 2012-12-26T15:10:10.300 回答
0

我有一个迭代 SQL 循环,我认为它的排序如下:

WHILE EXISTS (SELECT * FROM #child C INNER JOIN #parent P ON C.ParentID = P.SampleIDReplace WHERE P.SampleIDReplace > -1)
BEGIN
    UPDATE #child
    SET ParentID = SampleID
    FROM #parent 
    WHERE #child.ParentID = SampleIDReplace
END

基本上,while条件是比较子表中父ID列的内容,看父表的SampleIDReplace列是否有匹配值。如果有,它会去获取该记录的 SampleID。只有当连接导致每个 SampleIDReplace 都为 -1 时,它才会停止,这意味着我们无事可做。

在您的样本数据上,上述结果产生了预期的输出。

请注意,我必须在此处使用临时表而不是表变量,以便在循环中可以访问该表。如果您必须使用表变量,则需要做更多的手术。

显然,如果您有深层替换层次结构,那么您将进行大量更新,这可能是在针对生产数据库执行查询时需要考虑的问题。

于 2012-12-26T15:08:49.010 回答