/*
Author: Phil Factor
Original link: https://www.simple-talk.com/sql/sql-tools/automatically-creating-uml-database-diagrams-for-sql-server/
*/
/* we are just using this for chopping up a space-delimited list of database objects. If you
have embedded spaces in your object names then tough. You'll have to do it differently*/
IF EXISTS (Select * from sys.xml_schema_collections where name like 'ObjectListParameter')
DROP XML SCHEMA COLLECTION ObjectListParameter
GO
CREATE XML SCHEMA COLLECTION ObjectListParameter as '
'
GO
-- does a particular procedure exist
IF EXISTS ( SELECT 1 FROM sys.objects
WHERE object_ID= object_ID('dbo.usp_CreatePlantUMLCode'))
SET NOEXEC ON
GO
-- if the routine exists this isn't executed
CREATE PROCEDURE dbo.usp_CreatePlantUMLCode
AS Select 'created, but not implemented yet.'--just anything will do
GO
-- the following section will be always executed
SET NOEXEC OFF
GO
Alter procedure usp_CreatePlantUMLCode
@ObjectsToShow NVarchar(400)=null, -- space-delimited list of database objects
@dependenciesOf NVarchar(400)=null, --show the first order objects that reference or otherwise depend on it
@dependsOn NVarchar(400)=null, --show the objects that it depends on
@MyPlantUMLStatement varchar(max) output --the code to use for the diagram
/*
Examples of use:
Declare @ThePlantUMLCode Varchar(max)
execute usp_CreatePlantUMLCode @ObjectsToShow='HumanResources.employee person.person',
@MyPlantUMLStatement=@ThePlantUMLCode output
select @ThePlantUMLCode
Declare @ThePlantUMLCode Varchar(max)
execute usp_CreatePlantUMLCode @DependsOn='person.address',
@MyPlantUMLStatement=@ThePlantUMLCode output
select @ThePlantUMLCode
Declare @ThePlantUMLCode Varchar(max)
execute usp_CreatePlantUMLCode @DependenciesOf='person.address',
@MyPlantUMLStatement=@ThePlantUMLCode output
select @ThePlantUMLCode
Declare @ThePlantUMLCode Varchar(max)
execute usp_CreatePlantUMLCode @ObjectsToShow='HumanResources.employee person.person HumanResources.vEmployeeDepartmentHistory dbo.ufnGetContactInformation',
@MyPlantUMLStatement=@ThePlantUMLCode output
select @ThePlantUMLCode
execute usp_CreatePlantUMLCode @ObjectsToShow='dbo.ufnGetContactInformation',
@MyPlantUMLStatement=@ThePlantUMLCode output
select @ThePlantUMLCode
Declare @ThePlantUMLCode Varchar(max)
execute usp_CreatePlantUMLCode @ObjectsToShow='HumanResources.employee person.person HumanResources.vEmployeeDepartmentHistory ',
@MyPlantUMLStatement=@ThePlantUMLCode output
select @ThePlantUMLCode
execute usp_CreatePlantUMLCode @ObjectsToShow='HumanResources.vEmployeeDepartmentHistory dbo.ufnGetContactInformation',
@MyPlantUMLStatement=@ThePlantUMLCode output
select @ThePlantUMLCode
*/
as
--has the user given us a list of objects?
Declare @ObjectsToDo table(Object_ID int primary key) --check only specified once!
declare @xml_data xml(ObjectListParameter)
set @xml_data=''
Declare @ii int
Select @ii=0 --what has been specified in the parameters
if (@ObjectsToShow is not null) set @ii=@ii+1
if (@DependenciesOf is not null) set @ii=@ii+1
if (@DependsOn is not null) set @ii=@ii+1
if (@ii<1)
begin raiserror('Sorry, but you''ll need to specify what to draw!', 16,1);return;end
if (@ii>1)
begin raiserror('Sorry, only one parameter can be used at a time', 16,1);return;end
/* if he has given a list, then we need to parse the list and find the IDs of reach
database object that has been specified */
if @ObjectsToShow is not null
begin
insert into @ObjectsToDo
select object_ID(T.ref.value('.', 'sysname'))
from (Select @xml_data.query('
for $i in data(/Object) return
element item { $i }
')) A(list)
cross apply A.List.nodes('/item') T(ref)
end
--does the user want to see the dependencies
if @DependenciesOf is not null
begin --get all the foreign key references
insert into @ObjectsToDo
Select referenced_object_ID
from sys.foreign_keys
where parent_object_id=object_ID(@DependenciesOf)
union all --and all the objects that refer to it
Select referenced_ID from sys.sql_expression_dependencies
where referencing_id=object_ID(@DependenciesOf)
and referenced_ID is not null
and is_schema_bound_reference =0
union --and insert the object itself
Select object_ID(@DependenciesOf)
end
if @DependsOn is not null
begin --does the user want a diagram of all the objects that the object depends on?
insert into @ObjectsToDo --insert all the foreign key relationships
Select parent_object_ID
from sys.foreign_keys
where referenced_Object_id=object_ID(@DependsOn)
union all --and all the references this object makes
Select referencing_ID from sys.sql_expression_dependencies
where referenced_id=object_ID(@DependsOn)
and is_schema_bound_reference =0
union
Select object_ID(@DependsOn) --and add the object itself
end
Select @MyPlantUMLStatement='!define table(x) class x << (T,mistyrose) >>
!define view(x) class x << (V,lightblue) >>
!define table(x) class x << (T,mistyrose) >>
!define tr(x) class x << (R,red) >>
!define tf(x) class x << (F,darkorange) >>
!define af(x) class x << (F,white) >>
!define fn(x) class x << (F,plum) >>
!define fs(x) class x << (F,tan) >>
!define ft(x) class x << (F,wheat) >>
!define if(x) class x << (F,gaisboro) >>
!define p(x) class x << (P,indianred) >>
!define pc(x) class x << (P,lemonshiffon) >>
!define x(x) class x << (P,linen) >>
hide methods
hide stereotypes
skinparam classarrowcolor gray
'
/* firstly, we'll create all the UML table diagrams. */
Select @MyPlantUMLStatement=@MyPlantUMLStatement+ 'table('+object_schema_name(AllTables.object_ID)+'.'+Name+') {
' +(Select c.name+': '+t.name+ case when PrimaryKeyColumns.Object_ID is not null
then ' <>' else '' end
+ case when fk.parent_Object_ID is not null
then ' <>' else '' end+'
'
from sys.columns c --give the column names and the data types but no dimensions
inner join sys.types t
on c.user_type_id= t.user_type_id
Left outer join [sys].[foreign_key_columns] fk
on parent_object_ID=c.object_ID
and parent_column_ID=c.column_ID
Left outer join --the primary keys are a bit awkward to get
(Select i.object_ID, column_ID
from sys.indexes i
inner join sys.index_columns ic
on ic.object_ID=i.object_ID
and ic.index_ID=i.index_ID
inner join sys.key_constraints k
on k.parent_object_id=ic.object_ID
and i.index_ID=k.unique_index_ID
where ic.object_ID = AllTables.object_ID
and k.type='pk')PrimaryKeyColumns(Object_ID,Column_ID)
on c.object_ID=PrimaryKeyColumns.Object_ID
and c.column_ID=PrimaryKeyColumns.column_ID
where c.object_ID = AllTables.object_ID
FOR XML PATH(''), TYPE).value(N'(./text())[1]',N'varchar(max)')
/* so now we can add any triggers. We could do indexes as well
but I somehow felt this wasn't appropriate*/
+ coalesce('__ trigger __
'+ (Select name +'
'from sys.triggers where parent_ID=AllTables.object_ID
FOR XML PATH(''), TYPE).value('.', 'varchar(max)'),'')+'}
'
from sys.tables allTables
inner join @ObjectsToDo ObjectsToDo
on alltables.object_ID = ObjectsTodo.object_id
/* now let's do the views */
Select @MyPlantUMLStatement=@MyPlantUMLStatement
+ 'view('+object_schema_name(Allviews.object_ID)+'.'+Name+') {
' +(Select c.name+': '+t.name +'
'
from sys.columns c
inner join sys.types t
on c.user_type_id= t.user_type_id
where c.object_ID = AllViews.object_ID
FOR XML PATH(''), TYPE).value(N'(./text())[1]',N'varchar(max)')+'}
'
from sys.views allViews
inner join @ObjectsToDo ObjectsToDo
on allViews.object_ID = ObjectsTodo.object_id
/* now we do anything that is capable of having parameters */
Select @MyPlantUMLStatement=@MyPlantUMLStatement+
rtrim(lower(AllRoutines.type))+'('+object_schema_name(AllRoutines.object_ID)+'.'+AllRoutines.Name+') {
'
/* note, a routine can exist without a parameter */
+ coalesce((Select p.name+': '+TYPE_NAME(p.user_type_id) +'
'
FROM sys.objects AS o
INNER JOIN sys.parameters AS p ON o.object_id = p.object_id
where o.object_ID = AllRoutines.object_ID
FOR XML PATH(''), TYPE).value(N'(./text())[1]',N'varchar(max)'),'')+'}
'
from sys.objects Allroutines
inner join @ObjectsToDo ObjectsToDo
on Allroutines.object_ID = ObjectsTodo.object_id
and type in ('AF','FN','FS','FT','IF','P','PC','TF','X');
/* just the types that can have parameters */
/* and now that we have a class diagram for every object,
we now do the arrows.*/
Select @MyPlantUMLStatement= @MyPlantUMLStatement+
coalesce((Select
coalesce(object_schema_name(referencing_ID)+'.','')
+ object_name(referencing_ID) +' -|> '
+ referenced_schema_name+'.'+Referenced_Entity_name
+ ':References
'
--AS reference
FROM sys.sql_expression_dependencies
inner join @ObjectsToDo ObjectsToDo
on referencing_ID = ObjectsTodo.object_id
inner join @ObjectsToDo ObjectsToDo2
on referenced_ID = ObjectsTodo2.object_id
where is_schema_bound_reference =0
FOR XML PATH(''), TYPE).value(N'(./text())[1]',N'varchar(max)'),'')
Select @MyPlantUMLStatement= @MyPlantUMLStatement+
coalesce((Select Object_schema_name(parent_object_ID)+'.'+object_name(parent_object_ID)+' -|> '
+ Object_schema_name(referenced_object_ID)+'.'+object_name(referenced_object_ID)+':FK
'
from sys.foreign_keys
inner join @ObjectsToDo ObjectsToDo
on parent_object_ID = ObjectsTodo.object_id
inner join @ObjectsToDo ObjectsToDo2
on referenced_object_ID = ObjectsTodo2.object_id
FOR XML PATH(''), TYPE).value(N'(./text())[1]',N'varchar(max)'),'')
GO