在另一个question中,我询问了如何可视化存储在SQL Server数据库中的表中的分层数据。我找到了一种使用GraphViz可视化整个层次结构的方法,在T-SQL和Powershell中有一些管道。
我想使用这样的可视化来调试使用类似数据的应用程序。对于小的示例层次结构,可视化一切都很好。但在成千上万的层次结构中,这是压倒性的。
当我调试我的应用程序时,我通常只查看与给定节点相关的一小组节点。目前,对我来说,给定节点唯一重要的相关节点是后代和祖先,以及节点本身。
所以,我想要一种方法来只显示层次结构中作为给定节点的后代,祖先或自我的节点。
以下语句在链接问题中创建示例数据库和表。
CREATE DATABASE HierarchyTest;
GO
USE HierarchyTest;
GO
CREATE TABLE NodeHierarchy (
PK_NodeID INT NOT NULL
CONSTRAINT PK_NodeHierarchy PRIMARY KEY,
FK_ParentNodeID INT NULL
CONSTRAINT FK_NodeHierarchy_NodeHierarchy FOREIGN KEY
REFERENCES NodeHierarchy(PK_NodeID),
Name NVARCHAR(255) NOT NULL
);
以下语句使用国家/地区,城市和场所层次结构的修改版本填充表格。英国现在是根节点,有更多节点代表着名的英语场所。
INSERT INTO NodeHierarchy(PK_NodeID, FK_ParentNodeID, Name)
VALUES
(1, 18, N'Scotland'),
(2, 1, N'Glasgow'),
(3, 1, N'Edinburgh'),
(4, 1, N'St Andrews'),
(5, 2, N'The Barrowlands'),
(6, 2, N'The Cathouse'),
(7, 2, N'Carling Academy'),
(8, 2, N'SECC'),
(9, 2, N'King Tut''s Wah-Wah Hut'),
(10, 3, N'Henry''s Cellar Bar'),
(11, 3, N'The Bongo Club'),
(12, 3, N'Sneaky Pete''s'),
(13, 3, N'The Picture House'),
(14, 3, N'Potterrow'),
(15, 4, N'Aikman''s'),
(16, 4, N'The Union'),
(17, 4, N'Castle Sands'),
(18, NULL, N'United Kingdom'),
(19, 15, N'Upstairs'),
(20, 15, N'Downstairs'),
(21, 16, N'Venue 1'),
(22, 16, N'Venue 2'),
(23, 18, N'England'),
(24, 23, N'Manchester'),
(25, 24, N'Apollo Theatre'),
(26, 18, N'Liverpool'),
(27, 26, N'Cavern Club');
以下图片是链接问题中列出的Powershell脚本generate-graph.ps1
的输出。如果Stack Overflow缩小版本看起来很丑,请查看full-size image。
我只想看看圣安德鲁斯的后代和祖先是如何与之相关的。该图包含许多与这些关系无关的信息,因此难以阅读。当我将我的层次结构扩展到覆盖全球城市和场所的数千个节点时,完全可视化变得几乎无用。
在Freemind中,我画了一幅粗略的图表来反映:
如何仅提取与圣安德鲁斯相关的数据,以便将其提供给GraphViz?
答案 0 :(得分:0)
层次结构的自引用表示对于类似的工作来说有点笨拙 - 您只想选择一个分支,因此您需要以未知的次数递归地连接到目标表。很可能,但是当我在SQL Server中使用层次结构时,我会直接跳转到HierarchyId。
我不知道我们是否可以同时递归地同时查看 up 和 down 树;一个天真的方法对我来说失败了,所以我会提出一个更简单的选择。
您已拥有当前节点。获取该节点的子节点,然后获取该节点的父节点。联合他们,你就完成了。在SQL中进行递归连接的最简单方法是使用Common Table Expressions。
DECLARE @nodeid INT = 4
DECLARE @nodes TABLE (NodeID INT)
; WITH Parents (NodeID) AS
(
-- get the parent of the current node
SELECT FK_ParentNodeID FROM NodeHierarchy WHERE PK_NodeID = @nodeId
-- not sure if 'null' is a valid parent, but I'm assuming not
AND FK_ParentNodeID IS NOT NULL
UNION ALL
-- recursively get the parents of the parent
SELECT FK_ParentNodeID FROM NodeHierarchy
INNER JOIN Parents ON PK_NodeID = NodeID
WHERE FK_ParentNodeID IS NOT NULL
)
INSERT @nodes SELECT NodeID FROM Parents
; WITH Childs (NodeID) AS
(
-- selecting the current node
SELECT PK_NodeID FROM NodeHierarchy WHERE PK_NodeID = @nodeId
UNION ALL
-- recursively select the children of the branch
SELECT PK_NodeID FROM NodeHierarchy
INNER JOIN Childs ON FK_ParentNodeID = NodeID
)
INSERT @nodes SELECT NodeID FROM Childs
SELECT * FROM @nodes
现在根据您之前的问题,您只需从现有视图中进行选择。
SELECT Node, Label FROM NodeLabels
WHERE Node IN (SELECT NodeID FROM @nodes)
SELECT Parent, Child FROM Edges
WHERE Parent IN (SELECT NodeID FROM @nodes)
答案 1 :(得分:0)
我不认为你注意在这里使用工会,这是一种更简单的方式:
declare @nodeid int, @parentID int
select @nodeid = PK_NodeID, @parentID = FK_ParentNodeID
from NodeHierarchy where name = 'St Andrews'
select PK_NodeID, FK_ParentNodeID, Name
from NodeHierarchy
where PK_NodeID in (@nodeid, @parentID)
or FK_ParentNodeID = @nodeid
当然,你可以把它放在一个表格函数中,使它变得通用。