SQL邻接表查询
SQL adjacency list query
正在尝试对用户访问的服务的依赖关系进行建模。我创建了一个 parent/child 邻接列表样式 table,其中规定 'the service' 依赖组件 1,组件 1 依赖组件 2,等等,以及依赖类型,例如全部或部分
此图像显示布局 - 依赖关系图像
组件 1 和组件 9 的颜色不同,因为如果它们失败,那么整个服务就会失败。如果 Comp 2-9 中的任何一个失败,则服务将继续,但弹性会降低。
这是我用来创建 table
CREATE TABLE scratch
(
KeyID int PRIMARY KEY NOT NULL,
CompDesc varchar(30),
CompID int NOT NULL,
ReliesOn int NOT NULL,
RelianceType varchar(30),
)
INSERT scratch SELECT 0, 'Service', 0, 1, 'Full'
INSERT scratch SELECT 1, 'Component 1', 1, 2, 'Partial'
INSERT scratch SELECT 2, 'Component 1', 1, 3, 'Partial'
INSERT scratch SELECT 3, 'Component 1', 1, 4, 'Partial'
INSERT scratch SELECT 4, 'Component 4', 4, 5, 'Full'
INSERT scratch SELECT 5, 'Component 5', 5, 6, 'Full'
INSERT scratch SELECT 6, 'Component 6', 6, 7, 'Partial'
INSERT scratch SELECT 7, 'Component 6', 6, 8, 'Partial'
INSERT scratch SELECT 8, 'Component 2', 2, 9, 'Full'
INSERT scratch SELECT 9, 'Component 3', 3, 9, 'Full'
INSERT scratch SELECT 10, 'Component 7', 7, 9, 'Full'
INSERT scratch SELECT 11, 'Component 8', 8, 9, 'Full'
然后我可以 运行 一个非常粗略的查询来显示用户可以访问该服务的 4 个不同选项 -
SELECT t1.ReliesOn AS lev1, t2.ReliesOn as lev2, t3.ReliesOn as lev3, t4.ReliesOn as lev4, t5.ReliesOn as lev5, t6.ReliesOn as lev6
FROM Scratch AS t1
LEFT JOIN Scratch AS t2 ON t2.CompID = t1.ReliesOn
LEFT JOIN Scratch AS t3 ON t3.CompID = t2.ReliesOn
LEFT JOIN Scratch AS t4 ON t4.CompID = t3.ReliesOn
LEFT JOIN Scratch AS t5 ON t5.CompID = t4.ReliesOn
LEFT JOIN Scratch AS t6 ON t6.CompID = t5.ReliesOn
WHERE t1.ReliesOn = 1;
(抱歉,可能有更好的方法将查询串联在一起)
这个结果 -
lev1 lev2 lev3 lev4 lev5 lev6
1 2 9 NULL NULL NULL
1 3 9 NULL NULL NULL
1 4 5 6 7 9
1 4 5 6 8 9
我想做的是创建一个查询,我可以列出失败的组件并确定该服务是否仍然可用,例如
Failed Result
1 No Service
2,3 Reduced Resiliency
3,8 Reduced Resiliency
2,3,7,8 No Service
这只是一个非常简单的例子,我还有很多需要补充的,在大多数情况下,许多服务将依赖于相同的组件。
那么,您如何从失败的 comp 转到 up/across/down 依赖项以确定路径是否仍然存在?
希望这是有道理的
谢谢
显然,结果查询并不容易。我post给你一个方法,你可以玩玩SQL Fiddle
MS SQL Server 2014 架构设置:
CREATE TABLE scratch
(
KeyID int PRIMARY KEY NOT NULL,
CompDesc varchar(30),
CompID int NOT NULL,
ReliesOn int NULL, --null to allow node 9
RelianceType varchar(30),
)
INSERT scratch SELECT 0, 'Service', 0, 1, 'Full'
INSERT scratch SELECT 1, 'Component 1', 1, 2, 'Partial'
INSERT scratch SELECT 2, 'Component 1', 1, 3, 'Partial'
INSERT scratch SELECT 3, 'Component 1', 1, 4, 'Partial'
INSERT scratch SELECT 4, 'Component 4', 4, 5, 'Full'
INSERT scratch SELECT 5, 'Component 5', 5, 6, 'Full'
INSERT scratch SELECT 6, 'Component 6', 6, 7, 'Partial'
INSERT scratch SELECT 7, 'Component 6', 6, 8, 'Partial'
INSERT scratch SELECT 8, 'Component 2', 2, 9, 'Full'
INSERT scratch SELECT 9, 'Component 3', 3, 9, 'Full'
INSERT scratch SELECT 10, 'Component 7', 7, 9, 'Full'
INSERT scratch SELECT 11, 'Component 8', 8, 9, 'Full'
INSERT scratch SELECT 12, 'Component 9', 9, Null, 'Full' --node 9 added
这里是 查询 以计算活动路径:
with
from_to as ( select 9 as [from], 1 as [to] ),
failed_nodes as ( select 3 as f union select 4 ), --list of failed nodes
cte as (
select *
,CAST(CompID AS VARCHAR(255)) AS Path
from scratch
where CompId = (select [from] from from_to ) -easy: ReliesOn is null
union all
select s.*
,CAST(Path + '.' + CAST(s.CompID AS VARCHAR(255)) AS VARCHAR(255))
from scratch s
inner join cte on s.ReliesOn = cte.CompID
where s.compid not in ( select * from failed_nodes)
)
select * from cte
只需检查 Results 结果中的 keyID = 0
即可了解服务是否仍然可用以及活动路径(是一个简单的 where
):
| KeyID | CompDesc | CompID | ReliesOn | RelianceType | Path |
|-------|-------------|--------|----------|--------------|---------|
| 12 | Component 9 | 9 | (null) | Full | 9 |
| 8 | Component 2 | 2 | 9 | Full | 9.2 |
| 10 | Component 7 | 7 | 9 | Full | 9.7 |
| 11 | Component 8 | 8 | 9 | Full | 9.8 |
| 7 | Component 6 | 6 | 8 | Partial | 9.8.6 |
| 5 | Component 5 | 5 | 6 | Full | 9.8.6.5 |
| 6 | Component 6 | 6 | 7 | Partial | 9.7.6 |
| 5 | Component 5 | 5 | 6 | Full | 9.7.6.5 |
| 1 | Component 1 | 1 | 2 | Partial | 9.2.1 |
| 0 | Service | 0 | 1 | Full | 9.2.1.0 < still alive
此外,根据您的场景调整此解决方案以处理 RelianceType
个节点。
我设法编写了一个使用 Recursive CTEs 执行此操作的查询。
首先,我冒昧地在 scratch
table 中添加了两条记录:
INSERT scratch SELECT 12, 'Component 9', 9, 10, 'Full'
INSERT scratch SELECT 13, 'Users', 10, 10, 'Full'
这意味着:
- 9依赖10(User)如图
- 10 依赖于自身(这意味着在这种情况下 10 是终端组件 - 必须启动才能使服务可用)。
在查询中,不可用的组件应在 FailedComponents
CTE 中枚举其 ID:
with FailedComponents (id) as (
select id from (values
(1), (7), (8)
) x(id)
), OnlineComponents (compId, reliesOn) as (
select compId, reliesOn
from scratch
where compId = 0 -- <== entry point here
union all
select s.compId, s.reliesOn
from scratch s
join OnlineComponents c
on s.compId = c.reliesOn
where s.compId not in (select id from FailedComponents)
)
select case
when exists (select * from OnlineComponents where compId = reliesOn)
then 'Service is available'
else 'No Service'
end as [Status]
;
这里是 SQL Fiddle,您可以在其中尝试不同的值以查看查询如何处理它们。
请注意,查询不使用 scratch
table 中指定的 Full
/Partial
依赖类型。依赖类型是从上下文中推断出来的。基本上,如果有一种方法可以仅在可用组件上从 0 到 10 遍历图形,则整个服务是可用的。
还有一点警告:递归 CTE 的深度是有限制的。这称为 MAXRECURSION
,默认值为 100。如果需要,您需要将其更改为更高的值。
正在尝试对用户访问的服务的依赖关系进行建模。我创建了一个 parent/child 邻接列表样式 table,其中规定 'the service' 依赖组件 1,组件 1 依赖组件 2,等等,以及依赖类型,例如全部或部分
此图像显示布局 - 依赖关系图像
组件 1 和组件 9 的颜色不同,因为如果它们失败,那么整个服务就会失败。如果 Comp 2-9 中的任何一个失败,则服务将继续,但弹性会降低。
这是我用来创建 table
CREATE TABLE scratch
(
KeyID int PRIMARY KEY NOT NULL,
CompDesc varchar(30),
CompID int NOT NULL,
ReliesOn int NOT NULL,
RelianceType varchar(30),
)
INSERT scratch SELECT 0, 'Service', 0, 1, 'Full'
INSERT scratch SELECT 1, 'Component 1', 1, 2, 'Partial'
INSERT scratch SELECT 2, 'Component 1', 1, 3, 'Partial'
INSERT scratch SELECT 3, 'Component 1', 1, 4, 'Partial'
INSERT scratch SELECT 4, 'Component 4', 4, 5, 'Full'
INSERT scratch SELECT 5, 'Component 5', 5, 6, 'Full'
INSERT scratch SELECT 6, 'Component 6', 6, 7, 'Partial'
INSERT scratch SELECT 7, 'Component 6', 6, 8, 'Partial'
INSERT scratch SELECT 8, 'Component 2', 2, 9, 'Full'
INSERT scratch SELECT 9, 'Component 3', 3, 9, 'Full'
INSERT scratch SELECT 10, 'Component 7', 7, 9, 'Full'
INSERT scratch SELECT 11, 'Component 8', 8, 9, 'Full'
然后我可以 运行 一个非常粗略的查询来显示用户可以访问该服务的 4 个不同选项 -
SELECT t1.ReliesOn AS lev1, t2.ReliesOn as lev2, t3.ReliesOn as lev3, t4.ReliesOn as lev4, t5.ReliesOn as lev5, t6.ReliesOn as lev6
FROM Scratch AS t1
LEFT JOIN Scratch AS t2 ON t2.CompID = t1.ReliesOn
LEFT JOIN Scratch AS t3 ON t3.CompID = t2.ReliesOn
LEFT JOIN Scratch AS t4 ON t4.CompID = t3.ReliesOn
LEFT JOIN Scratch AS t5 ON t5.CompID = t4.ReliesOn
LEFT JOIN Scratch AS t6 ON t6.CompID = t5.ReliesOn
WHERE t1.ReliesOn = 1;
(抱歉,可能有更好的方法将查询串联在一起)
这个结果 -
lev1 lev2 lev3 lev4 lev5 lev6
1 2 9 NULL NULL NULL
1 3 9 NULL NULL NULL
1 4 5 6 7 9
1 4 5 6 8 9
我想做的是创建一个查询,我可以列出失败的组件并确定该服务是否仍然可用,例如
Failed Result
1 No Service
2,3 Reduced Resiliency
3,8 Reduced Resiliency
2,3,7,8 No Service
这只是一个非常简单的例子,我还有很多需要补充的,在大多数情况下,许多服务将依赖于相同的组件。
那么,您如何从失败的 comp 转到 up/across/down 依赖项以确定路径是否仍然存在?
希望这是有道理的
谢谢
显然,结果查询并不容易。我post给你一个方法,你可以玩玩SQL Fiddle
MS SQL Server 2014 架构设置:
CREATE TABLE scratch
(
KeyID int PRIMARY KEY NOT NULL,
CompDesc varchar(30),
CompID int NOT NULL,
ReliesOn int NULL, --null to allow node 9
RelianceType varchar(30),
)
INSERT scratch SELECT 0, 'Service', 0, 1, 'Full'
INSERT scratch SELECT 1, 'Component 1', 1, 2, 'Partial'
INSERT scratch SELECT 2, 'Component 1', 1, 3, 'Partial'
INSERT scratch SELECT 3, 'Component 1', 1, 4, 'Partial'
INSERT scratch SELECT 4, 'Component 4', 4, 5, 'Full'
INSERT scratch SELECT 5, 'Component 5', 5, 6, 'Full'
INSERT scratch SELECT 6, 'Component 6', 6, 7, 'Partial'
INSERT scratch SELECT 7, 'Component 6', 6, 8, 'Partial'
INSERT scratch SELECT 8, 'Component 2', 2, 9, 'Full'
INSERT scratch SELECT 9, 'Component 3', 3, 9, 'Full'
INSERT scratch SELECT 10, 'Component 7', 7, 9, 'Full'
INSERT scratch SELECT 11, 'Component 8', 8, 9, 'Full'
INSERT scratch SELECT 12, 'Component 9', 9, Null, 'Full' --node 9 added
这里是 查询 以计算活动路径:
with
from_to as ( select 9 as [from], 1 as [to] ),
failed_nodes as ( select 3 as f union select 4 ), --list of failed nodes
cte as (
select *
,CAST(CompID AS VARCHAR(255)) AS Path
from scratch
where CompId = (select [from] from from_to ) -easy: ReliesOn is null
union all
select s.*
,CAST(Path + '.' + CAST(s.CompID AS VARCHAR(255)) AS VARCHAR(255))
from scratch s
inner join cte on s.ReliesOn = cte.CompID
where s.compid not in ( select * from failed_nodes)
)
select * from cte
只需检查 Results 结果中的 keyID = 0
即可了解服务是否仍然可用以及活动路径(是一个简单的 where
):
| KeyID | CompDesc | CompID | ReliesOn | RelianceType | Path |
|-------|-------------|--------|----------|--------------|---------|
| 12 | Component 9 | 9 | (null) | Full | 9 |
| 8 | Component 2 | 2 | 9 | Full | 9.2 |
| 10 | Component 7 | 7 | 9 | Full | 9.7 |
| 11 | Component 8 | 8 | 9 | Full | 9.8 |
| 7 | Component 6 | 6 | 8 | Partial | 9.8.6 |
| 5 | Component 5 | 5 | 6 | Full | 9.8.6.5 |
| 6 | Component 6 | 6 | 7 | Partial | 9.7.6 |
| 5 | Component 5 | 5 | 6 | Full | 9.7.6.5 |
| 1 | Component 1 | 1 | 2 | Partial | 9.2.1 |
| 0 | Service | 0 | 1 | Full | 9.2.1.0 < still alive
此外,根据您的场景调整此解决方案以处理 RelianceType
个节点。
我设法编写了一个使用 Recursive CTEs 执行此操作的查询。
首先,我冒昧地在 scratch
table 中添加了两条记录:
INSERT scratch SELECT 12, 'Component 9', 9, 10, 'Full'
INSERT scratch SELECT 13, 'Users', 10, 10, 'Full'
这意味着:
- 9依赖10(User)如图
- 10 依赖于自身(这意味着在这种情况下 10 是终端组件 - 必须启动才能使服务可用)。
在查询中,不可用的组件应在 FailedComponents
CTE 中枚举其 ID:
with FailedComponents (id) as (
select id from (values
(1), (7), (8)
) x(id)
), OnlineComponents (compId, reliesOn) as (
select compId, reliesOn
from scratch
where compId = 0 -- <== entry point here
union all
select s.compId, s.reliesOn
from scratch s
join OnlineComponents c
on s.compId = c.reliesOn
where s.compId not in (select id from FailedComponents)
)
select case
when exists (select * from OnlineComponents where compId = reliesOn)
then 'Service is available'
else 'No Service'
end as [Status]
;
这里是 SQL Fiddle,您可以在其中尝试不同的值以查看查询如何处理它们。
请注意,查询不使用 scratch
table 中指定的 Full
/Partial
依赖类型。依赖类型是从上下文中推断出来的。基本上,如果有一种方法可以仅在可用组件上从 0 到 10 遍历图形,则整个服务是可用的。
还有一点警告:递归 CTE 的深度是有限制的。这称为 MAXRECURSION
,默认值为 100。如果需要,您需要将其更改为更高的值。