想象一下以下场景: 我有很多级别,从上级(根父级)到下级(子级或叶子级)。
(root parent) LEVEL 0
ID:98
/ \
/ \
/ \
o + LEVEL 1
ID:99 ID:100
/ \
/ \
o + LEVEL 2
ID:101 ID:102
/ \
/ \
o o LEVEL 3
ID:201 ID:202
想象一下,现在'+'符号就是房间。同级别的客房无法进行通信。每个房间都有一些门。通过这些大门,您可以与另一个级别的其他房间(孩子)沟通。
符号'o'是叶子,我的意思是说,没有门的房间可以进入较低楼层的其他房间。
为简单起见,每个房间有两个门,但可能有两个以上。
所以现在,最后想象如下:如果爆炸发生在属于父母房间的任何子/叶室中,那么父房间的所有门将自动关闭以防止爆炸传播到根父母。
想象一下下表:
ROOM_ID | PARENT_ROOM | GATES_OPEN | EXPLOSION
98 NULL 1 0
99 98 1 0
100 98 1 0
102 100 1 0
101 100 1 0
200 102 - 0
201 102 - 0
所有房间的所有大门都打开,因为最初没有爆炸。 200和201房间没有大门。
想象一下,每个房间都有一个传感器来检测可能的爆炸。如果传感器检测到爆炸,则信号传播到父室,并且父室关闭其所有门。这个信号也会传播到父母房间,所有的父母房间也会关闭所有的大门,依此类推,直到达到根父母,这也关闭了所有的大门。
所以现在想象房间ID:102会引起爆炸,所以我需要获得更新的下表:
ROOM_ID | PARENT_ROOM | GATES_OPEN | EXPLOSION
98 NULL 0 0
99 98 1 0
100 98 0 0
102 100 1 1
101 100 1 0
200 102 - 0
201 102 - 0
所以使用递归CTE,如何从初始表中获取最终表?我需要将它从导致爆炸的根传播到根父。
答案 0 :(得分:4)
这是一种方法:
首先,创建并填充样本表(请在将来的问题中保存此步骤):
DECLARE @T AS TABLE
(
ROOM_ID int,
PARENT_ROOM int,
GATES_OPEN bit,
EXPLOSION bit
)
INSERT INTO @T VALUES
(98, NULL, 1, 0),
(99, 98, 1, 0),
(100, 98, 1, 0),
(102, 100, 1, 0),
(101, 100, 1, 0),
(200, 102, NULL, 0),
(201, 102, NULL, 0)
然后,创建CTE:
DECLARE @RoomId int = 102;
;WITH CTE AS
(
SELECT ROOM_ID
,PARENT_ROOM
,GATES_OPEN
,CAST(1 AS BIT) AS EXPLOSION
FROM @T
WHERE ROOM_ID = @RoomId
UNION ALL
SELECT t.ROOM_ID
,t.PARENT_ROOM
,CAST(0 AS BIT) AS GATES_OPEN
,t.EXPLOSION
FROM @T t
INNER JOIN CTE ON t.ROOM_ID = CTE.PARENT_ROOM
)
更新表格:
UPDATE t
SET GATES_OPEN = CTE.GATES_OPEN,
EXPLOSION = CTE.EXPLOSION
FROM @T t
INNER JOIN CTE ON t.ROOM_ID = CTE.ROOM_Id
最后,测试更新是否正常:
SELECT *
FROM @T
结果:
ROOM_ID PARENT_ROOM GATES_OPEN EXPLOSION
98 NULL 0 0
99 98 1 0
100 98 0 0
102 100 1 1
101 100 1 0
200 102 NULL 0
201 102 NULL 0
如果您不知道爆炸发生在哪个房间(我猜测某些进程会更新数据库表并将爆炸值设置为1),那么您可以在表上使用触发器。它与我之前撰写的查询几乎相同,而且结果相同:
CREATE TRIGGER tr_Rooms_Update ON Rooms
FOR UPDATE
AS
;WITH CTE AS
(
SELECT ROOM_ID
,PARENT_ROOM
,GATES_OPEN
,EXPLOSION
FROM inserted
WHERE EXPLOSION = 1
UNION ALL
SELECT t.ROOM_ID
,t.PARENT_ROOM
,CAST(0 AS BIT) AS GATES_OPEN
,t.EXPLOSION
FROM Rooms t
INNER JOIN CTE ON t.ROOM_ID = CTE.PARENT_ROOM
)
UPDATE t
SET GATES_OPEN = CTE.GATES_OPEN,
EXPLOSION = CTE.EXPLOSION
FROM Rooms t
INNER JOIN CTE ON t.ROOM_ID = CTE.ROOM_Id
GO