我遇到这种情况,我需要配置现有的客户端数据,以解决我们的应用程序未正确更新表中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)
答案 0 :(得分: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
。
答案 1 :(得分: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)
答案 2 :(得分: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时停止,这意味着我们没有别的事情要做。
在您的样本数据上,上述结果会产生预期的输出。
请注意,我必须在此处使用临时表而不是表变量,以便在循环中访问表。如果你必须使用表变量,则需要进行更多的手术。
显然,如果您有深度替换层次结构,那么您将进行大量更新,这在考虑对生产数据库执行查询时可能需要考虑。