【发布时间】:2017-01-26 20:38:53
【问题描述】:
我有一张桌子,比如说 TestTable。此表有以下几列:
ID1 | ID2 | ID3 | LEVEL | PARENT_LEVEL | ENABLED | OBSOLET
所有列都是整数,ENABLED 和 OBSOLET 只有两个可能的值(0 或 1)
LEVEL 列可以有一个父级,这个父级可以有另一个父级等等,例如,想象下面的表格内容:
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',例如:
如果我将 LEVEL 99 更新为 ENABLED=1,他的父母也必须将 98 更新为 ENABLED=1,但不是 LEVEL 100 和 200。
如果我用 ENABLED=1 更新 LEVEL 200,他的父级也必须更新 100,并且必须用 ENABLED=1 更新 LEVEL 99 和 98,因为它们也有父级。
如果我使用 ENABLED=1 更新 LEVEL 300,则只有 LEVEL 300 会更新,因为它没有父级。
所以我需要一个递归更新查询来更新字段 ENABLED,直到 LEVEL 没有父级 (PARENT_LEVEL)。此外,我需要使用一个更新查询一次更新所有级别,而不仅仅是针对具体级别执行更新。
此外,在每次更新时,我都需要检查字段“OBSOLET”,如果 LEVEL 的字段 OBSOLET 设置为 1,则意味着必须进行回滚,例如,如果我更新 LEVEL,则考虑到上面的表格内容500到ENABLED=1,没问题,因为它的OBSOLET字段为0,所以它的ENABLED字段设置为1,然后通过递归,我们尝试更新它的父级,LEVEL 400,为ENABLED=1,但是因为设置了它的OBSOLET字段为 1 表示需要进行回滚,也就是说,LEVEL 400 的 ENABLED 字段保持为 0(未更新),而 level 500 的设置为 1 的字段 ENABLED 也应恢复为 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 并不意味着值已更改。这意味着它是更新语句中的一列。我怀疑如果值实际发生变化,您只会实际更新父母或孩子。为此,您必须将表与插入的表进行比较,以查看值是否不同。
-
对于手头的实际任务,似乎递归 cte(可能一个为 up 而另一个为 down)将是这里的解决方案。
-
@SeanLange 你能发布一个关于递归 CTE 的小例子吗?谢谢!
-
您可以尝试搜索。仅在 SO 上就有数千个示例,在任何搜索引擎上都有数十万个示例。
标签: sql-server sql-server-2008 triggers sql-server-2008-r2 recursive-query