{"id":2204,"date":"2016-05-03T00:00:00","date_gmt":"2016-04-15T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/automatically-creating-uml-database-diagrams-for-sql-server\/"},"modified":"2026-03-17T20:25:20","modified_gmt":"2026-03-17T20:25:20","slug":"automatically-creating-uml-database-diagrams-for-sql-server","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/tools-sql-server\/automatically-creating-uml-database-diagrams-for-sql-server\/","title":{"rendered":"SQL to UML: Auto-Generate Database Diagrams with PlantUML"},"content":{"rendered":"<div class=\"article-content\">\n<p>This article shows how to automatically generate UML database diagrams from SQL Server metadata using only open-source tools. The workflow: a T-SQL script queries sys.tables, sys.columns, sys.foreign_keys, and related metadata to generate PlantUML text, which PlantUML (using GraphViz) renders as PNG or SVG diagrams. The approach produces class diagrams (tables with columns and types), ER diagrams (showing foreign key relationships), and even sequence diagrams &#8211; all refreshable by re-running the script after database changes. No manual drawing required.<\/p>\n<h2>Introduction<\/h2>\n<p class=\"start\">Diagrams are often a great help in understanding how databases work. I&#8217;ve always wanted to generate various database diagrams automatically to go with my database documentation. The problem is that, once you&#8217;ve drawn them nicely in a drawing package, you feel reluctant to alter the database because you&#8217;d then have to re-draw the diagrams to reflect those changes. This article explains a way of creating easily &#8216;refreshed&#8217; automated diagrams based only on open-source, or free, tools.<\/p>\n<h1>PlantUML<\/h1>\n<p>We&#8217;ll be using PlantUML. PlantUML is an open-source project for writing Unified Modelling Language (UML) diagrams for Sequence, Use-Case, Class, Activity, Component, State and Objects. It uses GraphViz for its graphical output. Its use, like GraphViz, is to render quite complex diagrams from a simple intuitive language based on text.<\/p>\n<p>For example, this will give you a simple UML sequence diagram:<\/p>\n<pre>@startuml\nactor Phil #red\nactor Editor #green\nPhil -&gt; Editor: suggestion for an article\nEditor --&gt; Phil: wary response requesting details\nPhil -&gt; Editor: submits some pretty pictures\nEditor --&gt; Phil: Reluctant agreement\n@enduml\n<\/pre>\n<p>If this code is then processed by PlantUML, would provide this .PNG image:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-clip_image002.jpg\" alt=\"2405-clip_image002.jpg\" width=\"271\" height=\"317\" \/><\/p>\n<p>Don&#8217;t like the style? You can specify it how you like. With a little extra tweaking with the <code>skinparam<\/code>, you can get this instead<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-handwritten-diagram.png\" alt=\"2405-handwritten-diagram.png\" \/><\/p>\n<p>the code changes are simple<\/p>\n<pre>\t@startuml\nskinparam handwritten true\nskinparam monochrome true\nskinparam packageStyle rect\nskinparam defaultFontName Buxton Sketch\nskinparam shadowing false\n\nactor Phil #black\nactor Editor #grey\nPhil -&gt; Editor: Is the style a bit dated?\nEditor --&gt; Phil: Yes, it is a bit nineties\n\nPhil -&gt; Editor: How about a more handwritten 'arty' look?\nEditor --&gt; Phil: Sure, how about this?\n\n@enduml\n<\/pre>\n<p>Although you may have little urge to use UML diagrams in your everyday work, PlantUML is versatile enough to provide for a range of requirements. For a relational database person who winces at the terms &#8216;class&#8217; and &#8216;persistence&#8217;, even UML modelling has its uses as a way of communicating ideas and designs.<\/p>\n<p>In this article I&#8217;m going to show how it can be used to easily generate class diagrams for database objects. I will use TSQL code to create the PlantUML code directly.<\/p>\n<h1>Getting up and running<\/h1>\n<p>To get you started, there is a little <a href=\"http:\/\/plantuml.com\/plantuml\/uml\">browser application here<\/a> on the PlantUML site\u00a0 that allows you to type in PlantUML code and view the results. With that, and the PlantUML manual, you should be well away with using this interesting application. Chrome has an Add-in that runs PlantUML called the UML Diagram Editor.<\/p>\n<p>There are many ways of using PlantUML. There are several Atom packages that add PlantUML integration. If you use DocuWiki for documentation, there is an add-in for PlantUML, which is handy for teams since there is nothing to install on your machine then. If you want to use PlantUML from the command-line, there are <a href=\"http:\/\/plantuml.com\/starting.html\">install instructions here<\/a>. I use AsciiDocFx, which installs the prerequisites for you (though you may need to set the path to your GraphViz install. You can use your favourite editor via a command-line interface as well. PlantUML uses simple text-based instructions to render UML diagrams and these can be generated from SQL to show you such things as the intricacies of your database permission system, the foreign-key dependencies, or the details of your indexing strategy for a table.<\/p>\n<p>We&#8217;ll just stick to showing a subset of your database objects and the dependencies between them.<\/p>\n<h1>Data Modelling With Unified Modelling Language (UML)<\/h1>\n<p>Let&#8217;s look at AdventureWorks, just so we can try things out. I&#8217;ve always been caught out by views so it would be nice to see those objects in the database that a view references. Here is a simple diagram.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-clip_image004.jpg\" alt=\"2405-clip_image004.jpg\" width=\"554\" height=\"220\" \/><\/p>\n<p>Note that schemas have been represented as folders. All I did to do this was to use the text:<\/p>\n<pre>@startUML\n\u00a0\nhide empty members\nhide circle\nskinparam classarrowcolor gray\n\u00a0\nHumanResources.vEmployeeDepartmentHistory --|&gt; HumanResources.Department:References\nHumanResources.vEmployeeDepartmentHistory --|&gt; HumanResources.Employee:References\nHumanResources.vEmployeeDepartmentHistory --|&gt; HumanResources.EmployeeDepartmentHistory:References\nHumanResources.vEmployeeDepartmentHistory --|&gt; Person.Person:References\nHumanResources.vEmployeeDepartmentHistory --|&gt; HumanResources.Shift:References\n\u00a0\n@enduml\n\u00a0\n<\/pre>\n<p>I didn&#8217;t type this in. I just executed the SQL query that finds the soft references and foreign key references to and from the object that you name&#8230;<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">DECLARE @object_ID INT\nSELECT @Object_ID=object_id('HumanResources.vEmployeeDepartmentHistory')\nSELECT coalesce(object_schema_name(referencing_ID)+'.','')\n\u00a0\u00a0\u00a0\u00a0 + object_name(referencing_ID) +' --|&gt; '\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  + referenced_schema_name+'.'+Referenced_Entity_name\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  + ':References' \n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 --AS reference\nFROM sys.sql_expression_dependencies\n\u00a0\u00a0\u00a0 WHERE (referencing_id =@object_ID\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  OR referenced_ID = @object_ID)\n\u00a0\u00a0\u00a0\u00a0\u00a0 AND is_schema_bound_reference =0 \n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  and referenced_ID is not null\nUNION ALL\nSELECT coalesce(object_schema_name(parent_object_ID)+'.','') \n\u00a0\u00a0\u00a0 + object_name(parent_object_ID) + ' --|&gt; '\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 + coalesce(object_schema_name(referenced_object_ID)+'.','')\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 + object_name(referenced_object_ID)+':FK' \nFROM sys.foreign_keys\n\u00a0\u00a0\u00a0 WHERE parent_object_ID = @object_ID\n\u00a0\u00a0\u00a0\u00a0\u00a0 OR referenced_object_ID = @object_ID\n\u00a0\n<\/pre>\n<p>&#8230;and copied the result from the results pane (use text mode and set the &#8216;<i>Query<\/i>&#8216; &#8216;<i>Options<\/i>&#8216;<i> -&gt; &#8216;Results&#8217; -&gt; &#8216;Text&#8217; -&gt; &#8216;Maximum number of characters displayed in each column<\/i>&#8216; to 8192 or some other generous figure)<\/p>\n<p>&#8230;but you can of course use it with any other database object such as <b>dbo.ufnGetContactInformation. <\/b> Obviously, there is plenty more you can include but there is something to be said for having just one clear message to every diagram.<b> <\/b>Be wary about including too much in a diagram. Any network diagram quickly morphs into a birds-nest diagram if you try to do too much. This type of diagram was never intended to provide an entire map of your database.<\/p>\n<p>Once you&#8217;ve got some confidence, you can use PowerShell to update diagrams automatically. You just execute the code, save the result to disk, and pass the result to PlantUML, which has a command-line interface.<\/p>\n<p>Sometimes we would like to do a bit more than this. Now UML never had a stereotype to deal with a relational database, but there <a href=\"http:\/\/www.agiledata.org\/essays\/umlDataModelingProfile.html\"> is an established database profile<\/a> that includes a logical and physical model. As I have no strong feelings about the way that a table is represented, I&#8217;ve not deviated much from the standard class.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-clip_image006.jpg\" alt=\"2405-clip_image006.jpg\" width=\"554\" height=\"798\" \/><\/p>\n<p>We have drilled in to the detail from the first diagram here to look more closely at the tables. PlantUML is able to give you immense diagrams which are probably useless for our purpose here. If you want to do this, I&#8217;d advise you just to open the wallet a bit and use SQL Dependency Tracker instead. Here we want to keep things simpler, and just want to document tables, views, and functions and their dependencies in detail.<\/p>\n<p>The PlantUML source code that we used is this, generated from SQL code&#8230;<\/p>\n<pre class=\"\">!define table(x) class x &lt;&lt; (T,mistyrose) &gt;&gt; \n!define view(x) class x &lt;&lt; (V,lightblue) &gt;&gt; \n!define table(x) class x &lt;&lt; (T,mistyrose) &gt;&gt; \n!define tr(x) class x &lt;&lt; (R,red) &gt;&gt; \n!define tf(x) class x &lt;&lt; (F,darkorange) &gt;&gt; \n!define af(x) class x &lt;&lt; (F,white) &gt;&gt; \n!define fn(x) class x &lt;&lt; (F,plum) &gt;&gt; \n!define fs(x) class x &lt;&lt; (F,tan) &gt;&gt; \n!define ft(x) class x &lt;&lt; (F,wheat) &gt;&gt; \n!define if(x) class x &lt;&lt; (F,gaisboro) &gt;&gt; \n!define p(x) class x &lt;&lt; (P,indianred) &gt;&gt; \n!define pc(x) class x &lt;&lt; (P,lemonshiffon) &gt;&gt; \n!define x(x) class x &lt;&lt; (P,linen) &gt;&gt;\n\u00a0\nhide methods \nhide stereotypes \nskinparam classarrowcolor gray\n\u00a0\ntable(HumanResources.Employee) {\nBusinessEntityID: int &lt;&lt;pk&gt;&gt; &lt;&lt;fk&gt;&gt;\nNationalIDNumber: nvarchar\nLoginID: nvarchar\nOrganizationNode: hierarchyid\nOrganizationLevel: smallint\nJobTitle: nvarchar\nBirthDate: date\nMaritalStatus: nchar\nGender: nchar\nHireDate: date\nSalariedFlag: Flag\nVacationHours: smallint\nSickLeaveHours: smallint\nCurrentFlag: Flag\nrowguid: uniqueidentifier\nModifiedDate: datetime\n__ trigger __\ndEmployee\n}\ntable(Person.Person) {\nBusinessEntityID: int &lt;&lt;pk&gt;&gt; &lt;&lt;fk&gt;&gt;\nPersonType: nchar &lt;&lt;fk&gt;&gt;\nNameStyle: NameStyle\nTitle: nvarchar\nFirstName: Name\nMiddleName: Name\nLastName: Name\nSuffix: nvarchar\nEmailPromotion: int\nAdditionalContactInfo: xml\nDemographics: xml\nrowguid: uniqueidentifier\nModifiedDate: datetime\n__ trigger __\niuPerson\n}\nview(HumanResources.vEmployeeDepartmentHistory) {\nBusinessEntityID: int\nTitle: nvarchar\nFirstName: Name\nMiddleName: Name\nLastName: Name\nSuffix: nvarchar\nShift: Name\nDepartment: Name\nGroupName: Name\nStartDate: date\nEndDate: date\n}\ntf(dbo.ufnGetContactInformation) {\n@PersonID: int\n}\ndbo.ufnGetContactInformation -|&gt; HumanResources.Employee:References\ndbo.ufnGetContactInformation -|&gt; Person.Person:References\nHumanResources.vEmployeeDepartmentHistory -|&gt; HumanResources.Employee:References\nHumanResources.vEmployeeDepartmentHistory -|&gt; Person.Person:References\nHumanResources.Employee -|&gt; Person.Person:FK\n\u00a0\n<\/pre>\n<p>All we have done is to give a list of the tables that we want a diagram for.<\/p>\n<p>Here is the code:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">Declare @ThePlantUMLCode Varchar(max)\nexecute CreatePlantUMLCode @ObjectsToShow='HumanResources.employee person.person HumanResources.vEmployeeDepartmentHistory dbo.ufnGetContactInformation',\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @MyPlantUMLStatement=@ThePlantUMLCode output\nselect @ThePlantUMLCode\n\u00a0\n<\/pre>\n<p>I have only shown how to execute the stored procedure I wrote. I have specified the four objects whose dependencies and relationships I&#8217;m interested in. You may want to see all the dependencies and relationship of any object but even just the directly linked objects can overwhelm the diagram. It is better to be selective. PlantUML will do its best to oblige but you are soon at the wallchart scale of activity.<\/p>\n<p>This stored procedure can do other things. What if you want to see a UML diagram of all the objects that reference person.address Here, it is<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">Declare @ThePlantUMLCode Varchar(max)\nexecute CreatePlantUMLCode @DependsOn='person.address',\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @MyPlantUMLStatement=@ThePlantUMLCode output\nselect @ThePlantUMLCode\n<\/pre>\n<p>&#8230;giving you this, once the output is passed to PlantUML. &#8230;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-clip_image008.jpg\" alt=\"2405-clip_image008.jpg\" width=\"554\" height=\"427\" \/><\/p>\n<p>&#8230; or this &#8230;<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">Declare @ThePlantUMLCode Varchar(max)\nexecute CreatePlantUMLCode @DependenciesOf='person.address',\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 @MyPlantUMLStatement=@ThePlantUMLCode output\nselect @ThePlantUMLCode\nThePlantUMLCode\n<\/pre>\n<p>&#8230; giving you this &#8230; <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-clip_image009.png\" alt=\"2405-clip_image009.png\" width=\"460\" height=\"252\" \/><\/p>\n<p>I&#8217;ve given the SQL code at the end of the article.<\/p>\n<h1>Modifying your diagrams<\/h1>\n<p>In <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/dependencies-and-references-in-sql-server\/\">dependencies and references in SQL Server<\/a> and<a href=\"https:\/\/www.red-gate.com\/simple-talk\/blogs\/sql-naming-conventions\/\"> SQL naming conventions for readable diagrams<\/a>. I have taken quite a few design decisions which you may not like. Fortunately, PlantUML has an excellent PDF manual which explains that you can change almost everything. It is so good that I&#8217;m not going to attempt to repeat anything from it. However, be assured that diagrams can be altered to suit your requirements: Related objects can be placed side by side, or vertically;, You can use dotted arrows or arrows with a different head; You can opt to avoid shadows, and change the colour of anything. My instinct is to leave GraphViz and PlantUML to do as much as possible by default.<\/p>\n<p>PlantUML supports a flavour of markdown called Creole. This allows you to have formatted notes and descriptions. There is even a full set of icons from OpenIconic. Because PlantUML is based on GraphViz, you can use Graphviz raw. This allows you to do more general network visualisation diagrams for general software-engineering purposes. For database people, it is great for plotting out chains of dependencies in database data or in databases themselves.<\/p>\n<h1>GraphViz Dot Language<\/h1>\n<p>PlantUML can execute graphviz dot language files as well. We can therefore use PlantUML to draw &#8216;network&#8217; diagrams, which is something thet GraphViz does extraordinarily well. Most of the time, you&#8217;d probably want to do it with data but you can do it for your SQL Server metadata. Here, for example is Northwind&#8217;s table dependency diagram based on foreign key relationships<\/p>\n<pre>@startdot\ndigraph G { \nCustomerCustomerDemo -&gt; CustomerDemographics;\nCustomerCustomerDemo -&gt; Customers;\nEmployees -&gt; Employees;\nEmployeeTerritories -&gt; Employees;\nEmployeeTerritories -&gt; Territories;\nOrder Details -&gt; Orders;\nOrder Details -&gt; Products;\nOrders -&gt; Customers;\nOrders -&gt; Employees;\nOrders -&gt; Shippers;\nProducts -&gt; Categories;\nProducts -&gt; Suppliers;\nTerritories -&gt; Region;} \n@enddot\n\n<\/pre>\n<p>This code produced this diagram (you can click on it to enlarge it)<\/p>\n<p><a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-ninthdot-diagram.png\"> <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-clip_image011.png\" alt=\"2405-clip_image011.png\" width=\"553\" height=\"159\" \/><\/a><\/p>\n<p>..but you&#8217;ll see that I&#8217;m cheating a bit by using a very small sample database. Even AdventureWorks produces a big mess.<\/p>\n<p><a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-eleventhdot-diagram.png\"> <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-clip_image013.png\" alt=\"2405-clip_image013.png\" width=\"553\" height=\"79\" \/><\/a><\/p>\n<p>Which you can view if you have an image viewer that scrolls!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-clip_image015.jpg\" alt=\"2405-clip_image015.jpg\" width=\"554\" height=\"475\" \/><\/p>\n<p>How did I do this? Just the same way as the first database UML diagrams. I just executed this in SSMS, and copied the result into a text editor, just to put in the top and tail as shown above.<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">Select distinct\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 object_name(parent_object_ID) + ' -&gt; '\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 + object_name(referenced_object_ID)+';'\nFROM sys.foreign_keys\n<\/pre>\n<p>This can easily be refined into a stored procedure that takes out those illegal characters that make graphviz crash. GraphViz has a lot of power and it is worth looking around for examples and information.<br \/><strong>Read also: <\/strong><a href=\"https:\/\/www.red-gate.com\/simple-talk\/blogs\/visualizing-a-set-of-sql-server-graph-tables-by-generating-tgf-format\/\"><span data-sheets-root=\"1\">Visualizing graph tables with TGF<\/span><\/a><\/p>\n<h1>References<\/h1>\n<ul>\n<li><a href=\"http:\/\/www.graphviz.org\/\">http:\/\/www.graphviz.org\/<\/a>This is the main site for Graphviz. There is plenty of information here, and it is from this site that you can get the GraphViz application and documentation<\/li>\n<li><a href=\"http:\/\/plantuml.com\/\">http:\/\/plantuml.com\/<\/a> This is the PlantUML site. It has a large number of resources including downloads and documentation.<\/li>\n<li><a href=\"http:\/\/plantuml.com\/PlantUML_Language_Reference_Guide.pdf\">http:\/\/plantuml.com\/PlantUML_Language_Reference_Guide.pdf<\/a> The essential book of PlantUML. It is much less confusing to read than the PlantUML site that has some distracting google adverts.<\/li>\n<li><a href=\"https:\/\/graphviz.gitlab.io\/_pages\/pdf\/dotguide.pdf\">https:\/\/graphviz.gitlab.io\/_pages\/pdf\/dotguide.pdf<\/a>\u00a0The equivalent book of GraphViz. It was written some time ago but still seems relevant. <u> <\/u><\/li>\n<li><a href=\"http:\/\/asciidocfx.com\/\">http:\/\/asciidocfx.com\/<\/a> The site for ASCIIdocFX, which is a very cute and useful editor for ASCIIdoc (Markdown for grown-ups) which is intended for creating technical books with mixed text and diagrams. The editor is based on Atom, and you can use PlantUML, GraphViz and DITAA for drawing your diagrams.<\/li>\n<li><a href=\"https:\/\/www.dokuwiki.org\/dokuwiki\">https:\/\/www.dokuwiki.org\/dokuwiki#<\/a> DocuWiki is a wiki that is geared towards technical documentation. The real power of Docuwiki is in the add-ins, one of which gives you PlantUML diagrams embedded in your documentation.<\/li>\n<li><a href=\"http:\/\/www.agiledata.org\/essays\/umlDataModelingProfile.html\">http:\/\/www.agiledata.org\/essays\/umlDataModelingProfile.html<\/a> A specification for using UML diagrams for databases by Simple-Talk author Scott W Ambler<\/li>\n<\/ul>\n<h1>The Code<\/h1>\n<pre class=\"theme:ssms2012 lang:tsql decode:true \">\/* we are just using this for chopping up a space-delimited list of database objects. If you\nhave embedded spaces in your object names then tough. You'll have to do it differently*\/\n \nif exists (Select * from sys.xml_schema_collections where name like 'ObjectListParameter')\n  drop XML SCHEMA COLLECTION ObjectListParameter\ngo\ncreate xml schema collection ObjectListParameter as '\n&lt;xs:schema xmlns:xs=\"http:\/\/www.w3.org\/2001\/XMLSchema\"&gt;\n&lt;xs:element name=\"Object\"&gt;\n       &lt;xs:simpleType&gt;\n              &lt;xs:list itemType=\"xs:string\" \/&gt;\n       &lt;\/xs:simpleType&gt;\n&lt;\/xs:element&gt;\n&lt;\/xs:schema&gt;'\n \ngo\n\/*\n \n*\/\ngo\n    -- does a particular procedure  exist\nIF EXISTS ( SELECT 1 FROM sys.objects\n              WHERE  object_ID= object_ID('dbo.CreatePlantUMLCode'))\n  SET NOEXEC ON\nGO\n-- if the routine exists this isn't executed\nCREATE PROCEDURE dbo.CreatePlantUMLCode\nAS Select 'created, but not implemented yet.'--just anything will do\nGO\n-- the following section will be always executed\nSET NOEXEC OFF\nGO\nAlter procedure CreatePlantUMLCode\n@ObjectsToShow NVarchar(400)=null, -- space-delimited list of database objects\n@dependenciesOf NVarchar(400)=null, --show the first order objects that reference or otherwise depend on it\n@dependsOn  NVarchar(400)=null, --show the objects that it depends on\n@MyPlantUMLStatement varchar(max) output --the code to use for the diagram\n\/*\nExamples of use:\nDeclare @ThePlantUMLCode Varchar(max)\nexecute CreatePlantUMLCode @ObjectsToShow='HumanResources.employee person.person',\n       @MyPlantUMLStatement=@ThePlantUMLCode output\nselect @ThePlantUMLCode\n \nDeclare @ThePlantUMLCode Varchar(max)\nexecute CreatePlantUMLCode @DependsOn='person.address',\n       @MyPlantUMLStatement=@ThePlantUMLCode output\nselect @ThePlantUMLCode\n \nDeclare @ThePlantUMLCode Varchar(max)\nexecute CreatePlantUMLCode @DependenciesOf='person.address',\n       @MyPlantUMLStatement=@ThePlantUMLCode output\nselect @ThePlantUMLCode\n \nDeclare @ThePlantUMLCode Varchar(max)\nexecute CreatePlantUMLCode @ObjectsToShow='HumanResources.employee person.person HumanResources.vEmployeeDepartmentHistory  dbo.ufnGetContactInformation',\n       @MyPlantUMLStatement=@ThePlantUMLCode output\nselect @ThePlantUMLCode\n \nexecute CreatePlantUMLCode @ObjectsToShow='dbo.ufnGetContactInformation',\n       @MyPlantUMLStatement=@ThePlantUMLCode output\nselect @ThePlantUMLCode\n \nDeclare @ThePlantUMLCode Varchar(max)\nexecute CreatePlantUMLCode @ObjectsToShow='HumanResources.employee person.person HumanResources.vEmployeeDepartmentHistory ',\n       @MyPlantUMLStatement=@ThePlantUMLCode output\nselect @ThePlantUMLCode\n \nexecute CreatePlantUMLCode @ObjectsToShow='HumanResources.vEmployeeDepartmentHistory  dbo.ufnGetContactInformation',\n       @MyPlantUMLStatement=@ThePlantUMLCode output\nselect @ThePlantUMLCode\n*\/\nas\n--has the user given us a list of objects?\nDeclare @ObjectsToDo table(Object_ID int primary key) --check only specified once!\ndeclare @xml_data xml(ObjectListParameter)\nset @xml_data='&lt;Object&gt;'+ @ObjectsToShow +'&lt;\/Object&gt;'\nDeclare @ii int\nSelect @ii=0 --what has been specified in the parameters\nif (@ObjectsToShow is not null) set @ii=@ii+1\nif (@DependenciesOf is not null) set @ii=@ii+1\nif (@DependsOn is not null) set @ii=@ii+1\nif (@ii&lt;1)\n  begin raiserror('Sorry, but you''ll need  to specify what to draw!', 16,1);return;end\nif (@ii&gt;1)\n  begin raiserror('Sorry, only one parameter can be used at a time', 16,1);return;end\n\/* if he has given a list, then we need to parse the list and find the IDs of reach\ndatabase object that has been specified *\/\nif  @ObjectsToShow is not null\n       begin\n       insert into @ObjectsToDo\n         select object_ID(T.ref.value('.', 'sysname'))\n        from (Select @xml_data.query('\n                     for $i in data(\/Object) return\n                     element item { $i }\n              '))  A(list)\n        cross apply A.List.nodes('\/item') T(ref)\n       end\n--does the user want to see the dependencies\nif @DependenciesOf is not null\n       begin --get all the foreign key references\n       insert into @ObjectsToDo\n         Select referenced_object_ID\n           from sys.foreign_keys\n           where parent_object_id=object_ID(@DependenciesOf)\n       union all --and all the objects that refer to it\n         Select referenced_ID from sys.sql_expression_dependencies\n           where referencing_id=object_ID(@DependenciesOf)\n             and referenced_ID is not null\n             and is_schema_bound_reference =0\n    union --and insert the object itself\n         Select object_ID(@DependenciesOf)\n       end\nif @DependsOn is not null\n       begin --does the user want a diagram of all the objects that the object depends on?\n       insert into @ObjectsToDo --insert all the foreign key relationships\n      Select parent_object_ID\n        from sys.foreign_keys\n        where referenced_Object_id=object_ID(@DependsOn)\n      union all --and all the references this object makes\n      Select referencing_ID from sys.sql_expression_dependencies\n        where referenced_id=object_ID(@DependsOn)\n          and is_schema_bound_reference =0\n    union\n         Select object_ID(@DependsOn) --and add the object itself\n       end\n \nSelect @MyPlantUMLStatement='!define table(x) class x &lt;&lt; (T,mistyrose) &gt;&gt; \n!define view(x) class x &lt;&lt; (V,lightblue) &gt;&gt; \n!define table(x) class x &lt;&lt; (T,mistyrose) &gt;&gt;\n!define tr(x) class x &lt;&lt; (R,red) &gt;&gt;\n!define tf(x) class x &lt;&lt; (F,darkorange) &gt;&gt; \n!define af(x) class x &lt;&lt; (F,white) &gt;&gt; \n!define fn(x) class x &lt;&lt; (F,plum) &gt;&gt; \n!define fs(x) class x &lt;&lt; (F,tan) &gt;&gt; \n!define ft(x) class x &lt;&lt; (F,wheat) &gt;&gt; \n!define if(x) class x &lt;&lt; (F,gaisboro) &gt;&gt; \n!define p(x) class x &lt;&lt; (P,indianred) &gt;&gt; \n!define pc(x) class x &lt;&lt; (P,lemonshiffon) &gt;&gt; \n!define x(x) class x &lt;&lt; (P,linen) &gt;&gt;\n \nhide methods \nhide stereotypes\nskinparam classarrowcolor gray\n \n'\n\/* firstly, we'll create all the UML table diagrams. *\/\nSELECT @MyPlantUMLStatement =\n  @MyPlantUMLStatement + 'table(' + Object_Schema_Name(allTables.object_id) + '.' + name + ') { \n'\n  +\n  (\n  SELECT DISTINCT \n    c.name + ': ' + t.name\n       + CASE WHEN PrimaryKeyColumns.Object_ID IS NOT NULL THEN ' &lt;&lt;pk&gt;&gt;' ELSE '' END\n       + CASE WHEN fk.parent_object_id IS NOT NULL THEN ' &lt;&lt;fk&gt;&gt;' ELSE '' END + ' \n'\n    FROM sys.columns AS c --give the column names and the data types but no dimensions\n      INNER JOIN sys.types AS t\n        ON c.user_type_id = t.user_type_id\n      LEFT OUTER JOIN sys.foreign_key_columns AS fk\n        ON parent_object_id = c.object_id AND parent_column_id = c.column_id\n      LEFT OUTER JOIN --the primary keys are a bit awkward to get\n           (SELECT i.object_id, column_id\n              FROM sys.indexes AS i\n                INNER JOIN sys.index_columns AS ic\n                  ON ic.object_id = i.object_id AND ic.index_id = i.index_id\n                INNER JOIN sys.key_constraints AS k\n                  ON k.parent_object_id = ic.object_id AND i.index_id = k.unique_index_id\n              WHERE ic.object_id = allTables.object_id AND k.type = 'pk'\n           ) AS PrimaryKeyColumns(Object_ID, Column_ID)\n        ON c.object_id = PrimaryKeyColumns.Object_ID AND c.column_id = PrimaryKeyColumns.Column_ID\n    WHERE c.object_id = allTables.object_id\n  FOR XML PATH(''), TYPE\n  ).value(N'(.\/text())[1]', N'varchar(max)') \/* so now we can add any triggers. We could do indexes as well\nbut I somehow felt this wasn't appropriate*\/\n  + Coalesce('__ trigger __\n' +\n  (SELECT name + '\n'\n     FROM sys.triggers\n     WHERE parent_id = allTables.object_id\n  FOR XML PATH(''), TYPE\n  ).value('.', 'varchar(max)'), '') + '}\n'\n  FROM sys.tables AS allTables\n    INNER JOIN @ObjectsToDo AS ObjectsToDo\n      ON allTables.object_id = ObjectsToDo.Object_ID;\n\n\/* now let's do the views *\/\n \nSELECT @MyPlantUMLStatement =\n  @MyPlantUMLStatement + 'view(' + Object_Schema_Name(allViews.object_id) + '.' + name + ') {\n'                             +\n  (SELECT c.name + ': ' + t.name + '\n'\n     FROM sys.columns AS c\n       INNER JOIN sys.types AS t\n         ON c.user_type_id = t.user_type_id\n     WHERE c.object_id = allViews.object_id\n  FOR XML PATH(''), TYPE\n  ).value(N'(.\/text())[1]', N'varchar(max)') + '}\n'\n  FROM sys.views AS allViews\n    INNER JOIN @ObjectsToDo AS ObjectsToDo\n      ON allViews.object_id = ObjectsToDo.Object_ID;\n\n\/* now we do anything that is capable of having parameters *\/\nSELECT @MyPlantUMLStatement =\n  @MyPlantUMLStatement + RTrim(Lower(Allroutines.type)) + '('\n  + Object_Schema_Name(Allroutines.object_id) + '.' + Allroutines.name + ') {\n'\n  \/* note, a routine can exist without a parameter *\/\n  + Coalesce(\n  (SELECT p.name + ': ' + Type_Name(p.user_type_id) + '\n'\n     FROM sys.objects AS o\n       INNER JOIN sys.parameters AS p\n         ON o.object_id = p.object_id\n     WHERE o.object_id = Allroutines.object_id\n  FOR XML PATH(''), TYPE\n  ).value(N'(.\/text())[1]', N'varchar(max)'), '') + '}\n'\n  FROM sys.objects AS Allroutines\n    INNER JOIN @ObjectsToDo AS ObjectsToDo\n      ON Allroutines.object_id = ObjectsToDo.Object_ID AND type IN\n('AF', 'FN', 'FS', 'FT', 'IF', 'P', 'PC', 'TF', 'X');\n\n\/* just the types that can have parameters *\/\n\/* and now that we have a class diagram for every object,\nwe now do the arrows.*\/\nSELECT @MyPlantUMLStatement =\n  @MyPlantUMLStatement\n  + Coalesce(\n  (\n  SELECT Coalesce(Object_Schema_Name(referencing_id) + '.', '') + Object_Name(referencing_id)\n     + ' -|&gt; ' + referenced_schema_name + '.' + referenced_entity_name + ':References\n'\n    --AS reference\n    FROM sys.sql_expression_dependencies\n      INNER JOIN @ObjectsToDo AS ObjectsToDo\n        ON referencing_id = ObjectsToDo.Object_ID\n      INNER JOIN @ObjectsToDo AS ObjectsToDo2\n        ON referenced_id = ObjectsToDo2.Object_ID\n    WHERE is_schema_bound_reference = 0\n  FOR XML PATH(''), TYPE\n  ).value(N'(.\/text())[1]', N'varchar(max)'), '');\n\nSELECT @MyPlantUMLStatement =\n  @MyPlantUMLStatement\n  + Coalesce(\n  (\n  SELECT Object_Schema_Name(parent_object_id) + '.' + Object_Name(parent_object_id) + ' -|&gt; '\n     + Object_Schema_Name(referenced_object_id) + '.' + Object_Name(referenced_object_id)\n     + ':FK\n'\n    FROM sys.foreign_keys\n      INNER JOIN @ObjectsToDo AS ObjectsToDo\n        ON parent_object_id = ObjectsToDo.Object_ID\n      INNER JOIN @ObjectsToDo AS ObjectsToDo2\n        ON referenced_object_id = ObjectsToDo2.Object_ID\n  FOR XML PATH(''), TYPE\n  ).value(N'(.\/text())[1]', N'varchar(max)'), '');\n \ngo\n <\/pre>\n<h1>The Coda<\/h1>\n<p>Just to show that, with some care with the way that you do the arrows, you can get some very satisfactory effects.<\/p>\n<p class=\"illustration\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-Stairway1.png\" alt=\"2405-Stairway1.png\" width=\"627\" height=\"811\" \/><\/p>\n<p>&#8230; and PlantUML can even do syntax railroad diagrams for SQL.<\/p>\n<p class=\"illustration\"><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2405-syntax-diagram.png\" alt=\"2405-syntax-diagram.png\" \/><\/p>\n<\/div>\n\n\n<section id=\"my-first-block-block_e57621e2f631c2722ace438e5f079deb\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Fast, reliable and consistent SQL Server development&#8230;<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            &#8230;with SQL Toolbelt Essentials. 10 ingeniously simple tools for accelerating development, reducing risk, and standardizing workflows.                                    <\/div>\n            <\/div>\n                            <a href=\"https:\/\/www.red-gate.com\/products\/sql-toolbelt-essentials\/\" class=\"btn btn--secondary btn--lg\">Learn more &amp; try for free<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs: UML Database Diagrams in SQL Server<\/h2>\n\n                        <h3 class=\"mt-4xl\">1. How do you generate UML diagrams from a SQL Server database?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Run a T-SQL script that queries SQL Server system views (sys.tables, sys.columns, sys.foreign_keys) to generate PlantUML markup text. Save the output as a .puml file and process it with PlantUML (which uses GraphViz under the hood) to render PNG, SVG, or other image formats. The entire process can be automated so diagrams stay current as the database evolves.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">2. What is PlantUML and how does it work with SQL Server?<\/h3>\n            <div class=\"faq-answer\">\n                <p>PlantUML is an open-source tool that generates UML diagrams from plain text descriptions. For SQL Server, you write T-SQL that outputs PlantUML syntax describing your database objects and their relationships. PlantUML then renders visual diagrams via GraphViz. The text-based approach means diagrams are version-controllable, diffable, and automatically regeneratable.<\/p>\n            <\/div>\n                    <h3 class=\"mt-4xl\">3. Can you create ER diagrams from SQL Server metadata automatically?<\/h3>\n            <div class=\"faq-answer\">\n                <p>Yes. Query sys.foreign_keys to get relationships, sys.tables and sys.columns for entities and attributes, then format the output as PlantUML entity-relationship notation. The resulting diagram shows all tables, their columns with data types, and the foreign key relationships between them &#8211; automatically generated from the live database structure.<\/p>\n            <\/div>\n            <\/section>\n","protected":false},"excerpt":{"rendered":"<p>Auto-generate UML database diagrams from SQL Server metadata using PlantUML and GraphViz. T-SQL scripts create class diagrams, ER diagrams, and sequence diagrams from any database.&hellip;<\/p>\n","protected":false},"author":154613,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143532],"tags":[],"coauthors":[6813],"class_list":["post-2204","post","type-post","status-publish","format-standard","hentry","category-tools-sql-server"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2204","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/154613"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=2204"}],"version-history":[{"count":21,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2204\/revisions"}],"predecessor-version":[{"id":109262,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2204\/revisions\/109262"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=2204"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=2204"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=2204"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=2204"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}