What tool generates diagrams from SQL Server hierarchical data?

Export and run it through GraphViz - you don't even have to generate the hierarchy (just export nodes and edges) - just assign node names which are unique based on your NodeID column and use those same node names in the edges.

If you want something interactive, Microsoft has a Automatic Graph Layout library which can be used from .NET.

Here's an introduction to GraphViz.

What you are going to do is output a DOT file by exporting your SQL using a script like this: https://data.stackexchange.com/stackoverflow/q/109885/ which will run through GraphViz and generate your picture.

The DOT syntax is relatively simple - you can write it by hand first and then generate that from SQL and simply paste it in a file or something else (like .NET or PowerShell) which reads the SQL sets and generates the file.

You can automate that with SSIS. I made a package which wrote out the DOT file and ran graphviz on it and saved a graphiacl snapshot of our system on a daily basis.


I researched the leads in Cade Roux's answer and developed a solution using GraphViz.

To understand GraphViz, first I read this introductory article and the Command-line Invocation documentation. After successfully generating graphs from the example code listing in the article, I felt confident to work with my own data.

As Cade suggested, the best way to learn GraphViz's DOT language is to write it out myself. I studied the article's examples (Listings 1, 2, and 6) and then came up with this venues.gv to describe my own data:

digraph Venues
{ 
  N1[label = "Scotland"];
  N2[label = "Glasgow"];
  N3[label = "Edinburgh"];
  N4[label = "St Andrews"];
  N5[label = "The Barrowlands"];
  N6[label = "The Cathouse"];
  N7[label = "Carling Academy"];
  N8[label = "SECC"];
  N9[label = "King Tut's Wah-Wah Hut"];
  N10[label = "Henry's Cellar Bar"];
  N11[label = "The Bongo Club"];
  N12[label = "Sneaky Pete's"];
  N13[label = "The Picture House"];
  N14[label = "Potterrow"];
  N15[label = "Aikman's"];
  N16[label = "The Union"];
  N17[label = "Castle Sands"];

  N1 -> N2;
  N1 -> N3;
  N1 -> N4;
  N2 -> N5;
  N2 -> N6;
  N2 -> N7;
  N2 -> N8;
  N2 -> N9;
  N3 -> N10;
  N3 -> N11;
  N3 -> N12;
  N3 -> N13;
  N3 -> N14;
  N4 -> N15;
  N4 -> N16;
  N4 -> N17;
}

I fed this to circo, just one of the many graph-drawing commands that are part of GraphViz, and got pleasing output:

Output of circo -Tpng venues.gv -o venues.png:

Visualization of hierarchical venue data

The GraphViz file is structured in two blocks. One block declares a label for each node, and the other block declares the edges of the graph.

To provide the data for each of these blocks, I created a view of NodeHierarchy.

This view provides the data to declare labels for nodes:

CREATE VIEW NodeLabels (
  Node,
  Label
)
AS
SELECT
   PK_NodeID AS Node,
   Name AS Label
FROM
  NodeHierarchy;

This view provides the data to declare edges between nodes:

CREATE VIEW Edges (
  Parent,
  Child
)
AS
SELECT
  FK_ParentNodeID AS Parent,
  PK_NodeID AS Child
FROM NodeHierarchy
WHERE FK_ParentNodeID IS NOT NULL;

This Powershell script called generate-graph.ps1 selects the data from the views, transforms it into a GraphViz input, and pipes it to circo to produce a visualization of the full hierarchy like the one above:

"digraph Venues {" + (
  Invoke-Sqlcmd -Query "SELECT * FROM HierarchyTest.dbo.NodeLabels" | 
  ForEach-Object {"N" + $_.Node + "[label = """ + $_.Label + """];"}
) + (
  Invoke-Sqlcmd -Query "SELECT * FROM HierarchyTest.dbo.Edges" |
  ForEach-Object {"N" + $_.Parent + " -> N" + $_.Child + ";"}
) +
"}" | circo -Tpng -o venues.png

The script must be run in sqlps instead of powershell so that the Invoke-Sqlcmd cmdlet is available. The default working directory of sqlps is SQLSERVER, so I have to specify the drive when I run the script through sqlps.

This is the command I use to generate a graph like the one above:

sqlps C:.\generate-graph.ps1

This outputs a file called venues.png in the C working directory.

This Powershell solution feels a little inelegant, but this does what I need it to do. A more experienced Powershell programmer might be able to come up with something cleaner.