Sql server SQL Server:递归表更新
我有一张桌子,比如说TestTable。此表包含以下列: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
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,因为它没有父级
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