Sql server SQL Server:递归表更新

Sql server SQL Server:递归表更新,sql-server,sql-server-2008,triggers,sql-server-2008-r2,recursive-query,Sql Server,Sql Server 2008,Triggers,Sql Server 2008 R2,Recursive Query,我有一张桌子,比如说TestTable。此表包含以下列: ID1 | ID2 | ID3 | LEVEL | PARENT_LEVEL | ENABLED | OBSOLET 所有列都是整数,启用并仅废弃两个可能的值(0或1) 级别列可以有一个父级,该父级可以有另一个父级,以此类推,例如,想象一下以下表内容: ID1 | ID2 | ID3 | LEVEL | PARENT_LEVEL | ENABLED | OBSOLET 1 6 7 98 NULL

我有一张桌子,比如说TestTable。此表包含以下列:

ID1 | ID2 | ID3 | LEVEL | PARENT_LEVEL | ENABLED | OBSOLET
所有列都是整数,启用并仅废弃两个可能的值(0或1)

级别列可以有一个父级,该父级可以有另一个父级,以此类推,例如,想象一下以下表内容:

ID1 | ID2 | ID3 | LEVEL | PARENT_LEVEL | ENABLED | OBSOLET
 1     6     7      98       NULL          1         0
 1     6     6      99        98           1         0
 1     4     6     100        99           1         0
 1     2     3     200       100           1         0
 2     4     1     300       NULL          0         0
 3     3     4     400       NULL          0         1
 3     4     5     500       400           0         0
ID1、ID2和ID3是主键

在树中表示:

 + 98
    |__ 99
         |__ 100
              |___ 200

 + 300

 + 400
    |__ 500
200有100个作为父项,100有99个作为父项,99有98个作为父项。 300没有父母。 500有400个作为父项,400没有父项

因此,我需要一个更新查询来递归更新字段“ENABLED”,例如:

UPDATE [dbo].[TestTable] 
      SET ENABLED = 1
WHERE
      LEVEL IN (100,300,500); 
  • 如果我用ENABLED=1更新了级别99,那么他的父级98也必须更新为ENABLED=1,而不是级别100和200

  • 如果我用ENABLED=1更新了200级,也更新了他的父级,100级必须用ENABLED=1更新,还有99级和98级,因为它们也有父级

  • 如果我用ENABLED=1更新级别300,则只更新级别300,因为它没有父级

所以我需要一个递归更新查询来更新启用的字段,直到该级别没有父级(父级)。此外,我需要用一个更新查询一次更新所有级别,而不仅仅是对具体级别执行更新

此外,在每次更新时,我需要检查字段“OBSOLET”,如果某个级别的字段OBSOLET设置为1,则意味着必须进行回滚,例如,考虑到上面的表内容,如果我将级别500更新为ENABLED=1,则没有问题,因为其OBSOLET字段为0,因此其字段ENABLED设置为1,然后通过递归,我们尝试将其父级400级别更新为ENABLED=1,但由于其OBSOLET字段设置为1,这意味着需要进行回滚,即,级别400的ENABLED字段保持为0(未更新),级别500的ENABLED字段设置为1也应恢复为0

最后一个问题是,此更新查询应位于该表TestTable上的触发器内:

CREATE TRIGGER [dbo].[TG_TestTable]
ON  [dbo].[TestTable]  
FOR UPDATE
AS 
IF UPDATE ([ENABLED])
BEGIN

    // Update query must be here, so if field ENABLED is updated, trigger is fired again...so I don't know if disable trigger statement is necessary to be done before this update query and enable trigger after it.

END
这是因为要激活触发器,需要对TestTable表的某些行执行更新,例如:

UPDATE [dbo].[TestTable] 
      SET ENABLED = 1
WHERE
      LEVEL IN (100,300,500); 
因此,我尝试在触发器内进行更新查询,但不知道如何完成:

UPDATE [dbo].[TestTable] 
      SET ENABLED= inserted.ENABLED
      ..... // SOMETHING ELSE
    FROM inserted 
    WHERE
        [dbo].[TestTable].ID1 = inserted.ID1
        AND
        [dbo].[TestTable].ID2 = inserted.ID2
        AND
        [dbo].[TestTable].ID3 = inserted.ID3
        AND 
        [dbo].[TestTable].PARENT_LEVEL = inserted.LEVEL;

那么我如何才能做到这一点呢?可能使用递归函数或递归CTE?还是在执行时间和性能方面,在同一个表上使用递归触发器更好?欢迎提出所有想法。

我已经找到了解决方案。这本书很长,但很容易理解。
首先,创建并填充示例表(请在以后的问题中保存此步骤)

然后,创建一个
而不是UPDATE trigger
,该触发器将只更新符合条件的记录。
注意:这还将更新启用值未更改的记录,您很快就会在代码中看到它

应答码 结果:

ID1     ID2     ID3     LEVEL   PARENT_LEVEL    ENABLED     OBSOLET
1       6       7       98      NULL            1           0
1       6       6       99      98              0           0
1       4       6       100     99              0           0
1       2       3       200     100             0           0
2       4       1       300     NULL            0           0
3       3       4       400     NULL            0           1
3       4       5       500     400             0           0
ID1     ID2     ID3     LEVEL   PARENT_LEVEL    ENABLED     OBSOLET
1       6       7       98      NULL            1           0
1       6       6       99      98              1           0
1       4       6       100     99              1           0
1       2       3       200     100             1           0
2       4       1       300     NULL            0           0
3       3       4       400     NULL            0           1
3       4       5       500     400             0           1
执行以下几项更新:

UPDATE TestTable
SET ENABLED = 1
WHERE LEVEL IN(200, 500)

UPDATE TestTable
SET ENABLED = 1,
    OBSOLET = 1
WHERE LEVEL = 500
测试结果:

结果:

ID1     ID2     ID3     LEVEL   PARENT_LEVEL    ENABLED     OBSOLET
1       6       7       98      NULL            1           0
1       6       6       99      98              0           0
1       4       6       100     99              0           0
1       2       3       200     100             0           0
2       4       1       300     NULL            0           0
3       3       4       400     NULL            0           1
3       4       5       500     400             0           0
ID1     ID2     ID3     LEVEL   PARENT_LEVEL    ENABLED     OBSOLET
1       6       7       98      NULL            1           0
1       6       6       99      98              1           0
1       4       6       100     99              1           0
1       2       3       200     100             1           0
2       4       1       300     NULL            0           0
3       3       4       400     NULL            0           1
3       4       5       500     400             0           1

您真正需要注意的一点是,更新并不意味着值已更改。这意味着它是update语句中的一列。我猜想只有当值发生实际更改时,才需要实际更新父对象或子对象。为此,您必须将表与inserted进行比较,以查看值是否不同。对于手头的实际任务,这里的解决方案似乎是递归cte(可能是一个为up编写的cte,另一个为down编写的cte)。@SeanLange您可以发布一个关于递归cte的小示例吗?谢谢你可以试着搜索。这里有成千上万的例子,任何搜索引擎都有成千上万的例子。@user1624552你测试过这个答案吗?
ID1     ID2     ID3     LEVEL   PARENT_LEVEL    ENABLED     OBSOLET
1       6       7       98      NULL            1           0
1       6       6       99      98              1           0
1       4       6       100     99              1           0
1       2       3       200     100             1           0
2       4       1       300     NULL            0           0
3       3       4       400     NULL            0           1
3       4       5       500     400             0           1