SQL Server Graph:如何识别子图?
Posted
技术标签:
【中文标题】SQL Server Graph:如何识别子图?【英文标题】:SQL Server Graph: how identify subgraphs? 【发布时间】:2020-12-25 06:18:27 【问题描述】:我有 [Friends] 节点和 [Link] 边缘。我的任务是找出哪个朋友属于哪个子图(网络)。对此的简单说明:
这是我如何构造节点、边和填充数据的代码:
-- Construct nodes
DROP TABLE IF EXISTS [dbo].[Friend];
CREATE TABLE [dbo].[Friend] (
[FriendId] INT IDENTITY PRIMARY KEY
, [Name] NVARCHAR(256) NOT NULL
) AS NODE;
INSERT INTO [dbo].[Friend]([Name]) VALUES
('Jon'), ('Rita'), ('Ben'), ('Andrew'), ('Joe'), ('Patrick')
, ('Mike'), ('Thomas'), ('Kelly'), ('Lily'), ('Kira');
-- Construct edges
DROP TABLE IF EXISTS [dbo].[Link];
CREATE TABLE [dbo].[Link] (
[LinkDirection] BIT NOT NULL
, [FriendIdFrom] INT NOT NULL
, [FriendIdTo] INT NOT NULL
)AS EDGE;
INSERT INTO [dbo].[Link] ($from_id, $to_id, [LinkDirection], [FriendIdFrom], [FriendIdTo])
-- Network 1
SELECT [f1].$node_id, [f2].$node_id, 1, [f1].[FriendId], [f2].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Jon' AND [f2].[Name] = 'Rita'
UNION ALL SELECT [f1].$node_id, [f2].$node_id, 1, [f1].[FriendId], [f2].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Jon' AND [f2].[Name] = 'Ben'
UNION ALL SELECT [f1].$node_id, [f2].$node_id, 1, [f1].[FriendId], [f2].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Jon' AND [f2].[Name] = 'Andrew'
UNION ALL SELECT [f1].$node_id, [f2].$node_id, 1, [f1].[FriendId], [f2].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Andrew' AND [f2].[Name] = 'Joe'
UNION ALL SELECT [f1].$node_id, [f2].$node_id, 1, [f1].[FriendId], [f2].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Andrew' AND [f2].[Name] = 'Patrick'
-- Network 2
UNION ALL SELECT [f1].$node_id, [f2].$node_id, 1, [f1].[FriendId], [f2].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Mike' AND [f2].[Name] = 'Thomas'
UNION ALL SELECT [f1].$node_id, [f2].$node_id, 1, [f1].[FriendId], [f2].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Mike' AND [f2].[Name] = 'Kelly'
-- Network 3
UNION ALL SELECT [f1].$node_id, [f2].$node_id, 1, [f1].[FriendId], [f2].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Lily' AND [f2].[Name] = 'Kira'
-- To have bi-directional link
-- Network 1
UNION ALL SELECT [f2].$node_id, [f1].$node_id, 0, [f2].[FriendId], [f1].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Jon' AND [f2].[Name] = 'Rita'
UNION ALL SELECT [f2].$node_id, [f1].$node_id, 0, [f2].[FriendId], [f1].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Jon' AND [f2].[Name] = 'Ben'
UNION ALL SELECT [f2].$node_id, [f1].$node_id, 0, [f2].[FriendId], [f1].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Jon' AND [f2].[Name] = 'Andrew'
UNION ALL SELECT [f2].$node_id, [f1].$node_id, 0, [f2].[FriendId], [f1].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Andrew' AND [f2].[Name] = 'Joe'
UNION ALL SELECT [f2].$node_id, [f1].$node_id, 0, [f2].[FriendId], [f1].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Andrew' AND [f2].[Name] = 'Patrick'
-- Network 2
UNION ALL SELECT [f2].$node_id, [f1].$node_id, 0, [f2].[FriendId], [f1].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Mike' AND [f2].[Name] = 'Thomas'
UNION ALL SELECT [f2].$node_id, [f1].$node_id, 0, [f2].[FriendId], [f1].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Mike' AND [f2].[Name] = 'Kelly'
-- Network 3
UNION ALL SELECT [f2].$node_id, [f1].$node_id, 0, [f2].[FriendId], [f1].[FriendId] FROM [dbo].[Friend] [f1] INNER JOIN [dbo].[Friend] [f2] ON [f1].[Name] = 'Lily' AND [f2].[Name] = 'Kira';
我有代码,如何列出所有可能的路径:
SELECT
[StartNode] = [f1].[Name]
, [FinalNode] = LAST_VALUE([f2].[name]) WITHIN GROUP (GRAPH PATH)
, [Steps] = COUNT([f2].[FriendId]) WITHIN GROUP (GRAPH PATH)
, [Path] = [f1].[Name] + ' => ' + STRING_AGG([f2].[Name],' => ') WITHIN GROUP (GRAPH PATH)
, [Network] = '???'
--, *
FROM
[dbo].[Friend] [f1]
, [dbo].[Friend] FOR PATH [f2]
, [dbo].[Link] FOR PATH [l]
WHERE
MATCH(SHORTEST_PATH(f1(-(l)->f2)+));
但是,我很难在这里添加网络标识符(唯一的数字,文本,无关紧要):( 也许有人有以前的经验,可以提供帮助?
预期结果是:
---------------------------------------------------------------------------------
|StartNode | FinalNode | Steps | Path |Network|
|------------| -----------| -------| -----------------------------------|-------|
|Jon | Rita | 1 | Jon => Rita |1 |
|Jon | Ben | 1 | Jon => Ben |1 |
|Jon | Andrew | 1 | Jon => Andrew |1 |
|Rita | Jon | 1 | Rita => Jon |1 |
|Ben | Jon | 1 | Ben => Jon |1 |
|Andrew | Jon | 1 | Andrew => Jon |1 |
|Andrew | Joe | 1 | Andrew => Joe |1 |
|Andrew | Patrick | 1 | Andrew => Patrick |1 |
|Joe | Andrew | 1 | Joe => Andrew |1 |
|Patrick | Andrew | 1 | Patrick => Andrew |1 |
|Mike | Thomas | 1 | Mike => Thomas |2 |
|Mike | Kelly | 1 | Mike => Kelly |2 |
|Thomas | Mike | 1 | Thomas => Mike |2 |
|Kelly | Mike | 1 | Kelly => Mike |2 |
|Lily | Kira | 1 | Lily => Kira |3 |
|Kira | Lily | 1 | Kira => Lily |3 |
|Jon | Jon | 2 | Jon => Andrew => Jon |1 |
|Jon | Joe | 2 | Jon => Andrew => Joe |1 |
|Jon | Patrick | 2 | Jon => Andrew => Patrick |1 |
|Rita | Rita | 2 | Rita => Jon => Rita |1 |
|Rita | Ben | 2 | Rita => Jon => Ben |1 |
|Rita | Andrew | 2 | Rita => Jon => Andrew |1 |
|Ben | Rita | 2 | Ben => Jon => Rita |1 |
|Ben | Ben | 2 | Ben => Jon => Ben |1 |
|Ben | Andrew | 2 | Ben => Jon => Andrew |1 |
|Andrew | Rita | 2 | Andrew => Jon => Rita |1 |
|Andrew | Ben | 2 | Andrew => Jon => Ben |1 |
|Andrew | Andrew | 2 | Andrew => Jon => Andrew |1 |
|Joe | Jon | 2 | Joe => Andrew => Jon |1 |
|Joe | Joe | 2 | Joe => Andrew => Joe |1 |
|Joe | Patrick | 2 | Joe => Andrew => Patrick |1 |
|Patrick | Jon | 2 | Patrick => Andrew => Jon |1 |
|Patrick | Joe | 2 | Patrick => Andrew => Joe |1 |
|Patrick | Patrick | 2 | Patrick => Andrew => Patrick |1 |
|Mike | Mike | 2 | Mike => Thomas => Mike |2 |
|Thomas | Thomas | 2 | Thomas => Mike => Thomas |2 |
|Thomas | Kelly | 2 | Thomas => Mike => Kelly |2 |
|Kelly | Thomas | 2 | Kelly => Mike => Thomas |2 |
|Kelly | Kelly | 2 | Kelly => Mike => Kelly |2 |
|Lily | Lily | 2 | Lily => Kira => Lily |3 |
|Kira | Kira | 2 | Kira => Lily => Kira |3 |
|Rita | Joe | 3 | Rita => Jon => Andrew => Joe |1 |
|Rita | Patrick | 3 | Rita => Jon => Andrew => Patrick |1 |
|Ben | Joe | 3 | Ben => Jon => Andrew => Joe |1 |
|Ben | Patrick | 3 | Ben => Jon => Andrew => Patrick |1 |
|Joe | Rita | 3 | Joe => Andrew => Jon => Rita |1 |
|Joe | Ben | 3 | Joe => Andrew => Jon => Ben |1 |
|Patrick | Rita | 3 | Patrick => Andrew => Jon => Rita |1 |
|Patrick | Ben | 3 | Patrick => Andrew => Jon => Ben |1 |
---------------------------------------------------------------------------------
简而言之,我需要方法来为这些网络赋予唯一的名称,并与人进行映射。
【问题讨论】:
【参考方案1】:select *,
min([FinalNode]) over(partition by [StartNode]) as NetworkOf,
----in case of duplicate names, also use partition by StartNodeId instead of [StartNode]...
min(FinalFriendId) over(partition by [StartNode]) as NetworkId
from
(
SELECT
[StartNode] = [f1].[Name]
, [FinalNode] = LAST_VALUE([f2].[name]) WITHIN GROUP (GRAPH PATH)
, [Steps] = COUNT([f2].[FriendId]) WITHIN GROUP (GRAPH PATH)
, [Path] = [f1].[Name] + ' => ' + STRING_AGG([f2].[Name],' => ') WITHIN GROUP (GRAPH PATH)
, FinalFriendId = LAST_VALUE(f2.FriendId) WITHIN GROUP (GRAPH PATH)
FROM
[dbo].[Friend] [f1]
, [dbo].[Friend] FOR PATH [f2]
, [dbo].[Link] FOR PATH [l]
WHERE
MATCH(SHORTEST_PATH(f1(-(l)->f2)+))
) as src;
【讨论】:
谢谢你,非常尊重。现在我们有了简单的方法来检测 SQL Server 中所有断开连接的图 :)以上是关于SQL Server Graph:如何识别子图?的主要内容,如果未能解决你的问题,请参考以下文章