需要更新SQL Server中Master Parent行中的最后一个子行值

时间:2014-06-26 14:15:06

标签: sql sql-server sql-server-2008

我需要将最后一个子记录值更新为SQL中的父记录。我卡住了,并且没有点击任何想法来解决这个问题。

我有这样的表结构。

| id | parentID | Val|

  1      NULL      NULL
  2       1         0
  3       2         0
  4       3         0
  5       4         10
  10     NULL      NULL
  11      11        101

我还附上了表脚本。

create table #table1 (id int, parentId int,Val int)

INSERT INTO #table1 VALUES (1,null,null),
(2,1,0)
,(3,2,0)
,(4,3,0)
,(5,4,10)
,(10, null,null)
,(11,10,101)

预期结果将是

| id | parentID | Val|

  1      NULL      10
  10     NULL      101

1 个答案:

答案 0 :(得分:0)

我认为这就是你要求的:

WITH CTE1 (MasterParentID, ChildID, Length) AS
(
    SELECT ID, ID, 0
    FROM #table1
    WHERE ParentID IS NULL
    UNION ALL
    SELECT CTE1.MasterParentID, T.ID, CTE1.Length + 1
    FROM CTE1 JOIN #table1 T ON CTE1.ChildID = T.ParentID
),
CTE2 AS
(
    SELECT
        MasterParentID,
        LastChildID = ChildID,
        Rank = ROW_NUMBER() OVER (PARTITION BY MasterParentID ORDER BY Length DESC)
    FROM CTE1
)
UPDATE #table1
SET Val = (SELECT Val FROM #table1 C WHERE C.ID = CTE2.LastChildID)
FROM CTE2 JOIN #table1 P ON CTE2.MasterParentID = P.ID
WHERE CTE2.Rank = 1

这将第1行的值设置为10,将第10行的值设置为101。

解释

为了解释这个陈述,我将逐步建立起来。

步骤1:找到每个子行的主父行。

WITH CTE1 (MasterParentID, ChildID, Length) AS
(
    SELECT ID, ID, 0
    FROM #table1
    WHERE ParentID IS NULL
    UNION ALL
    SELECT CTE1.MasterParentID, T.ID, CTE1.Length + 1
    FROM CTE1 JOIN #table1 T ON CTE1.ChildID = T.ParentID
)
SELECT * FROM CTE1

此查询使用common table expression(CTE)以递归方式将父行连接到子行。结果集是:

MasterParentID ChildID Length
1              1       0
10             10      0
10             11      1
1              2       1
1              3       2
1              4       3
1              5       4

前两行由SELECT ID, ID, 0 FROM #table1 WHERE ParentID IS NULL返回。通过递归地将前两行连接到#table1来返回剩余的行。

Length列表示从MasterParentIDChildID的链的长度。它将在步骤2中用于识别每个链中的最后一个链接。

步骤2:查找每个主父行的最后一个子行。

WITH CTE1 (MasterParentID, ChildID, Length) AS
(
    /* as above... */
),
CTE2 AS
(
    SELECT
        MasterParentID,
        LastChildID = ChildID,
        Rank = ROW_NUMBER() OVER (PARTITION BY MasterParentID ORDER BY Length DESC)
    FROM CTE1
)
SELECT *
FROM CTE2
WHERE CTE2.Rank = 1

此查询采用步骤1中的结果集,按MasterParentID对其进行分区,并仅返回每个分区中Length(排名#1)最高的行。结果集是:

MasterParentID LastChildID Rank
1              5           1
10             11          1

步骤3:使用其最后一个子行的值更新每个主父行的值。

WITH CTE1 (MasterParentID, ChildID, Length) AS
(
    /* as above... */
),
CTE2 AS
(
    /* as above... */
)
UPDATE #table1
SET Val = (SELECT Val FROM #table1 C WHERE C.ID = CTE2.LastChildID)
FROM CTE2 JOIN #table1 P ON CTE2.MasterParentID = P.ID
WHERE CTE2.Rank = 1

此语句采用步骤2中的结果集,并使用它来更新#table1