SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
IF EXISTS (SELECT * FROM sys.fn_listextendedproperty(N'Description' , N'SCHEMA',N'dbo', N'PROCEDURE',N'sp_doc', NULL,NULL))
BEGIN;
EXEC sys.sp_dropextendedproperty @name=N'Description' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
END
GO
IF EXISTS (SELECT * FROM sys.fn_listextendedproperty(N'@SqlMinorVersion' , N'SCHEMA',N'dbo', N'PROCEDURE',N'sp_doc', NULL,NULL))
BEGIN;
EXEC sys.sp_dropextendedproperty @name=N'@SqlMinorVersion' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
END
GO
IF EXISTS (SELECT * FROM sys.fn_listextendedproperty(N'@SqlMajorVersion' , N'SCHEMA',N'dbo', N'PROCEDURE',N'sp_doc', NULL,NULL))
BEGIN;
EXEC sys.sp_dropextendedproperty @name=N'@SqlMajorVersion' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
END
GO
IF EXISTS (SELECT * FROM sys.fn_listextendedproperty(N'@ExtendedPropertyName' , N'SCHEMA',N'dbo', N'PROCEDURE',N'sp_doc', NULL,NULL))
BEGIN;
EXEC sys.sp_dropextendedproperty @name=N'@ExtendedPropertyName' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
END
GO
IF EXISTS (SELECT * FROM sys.fn_listextendedproperty(N'@DatabaseName' , N'SCHEMA',N'dbo', N'PROCEDURE',N'sp_doc', NULL,NULL))
BEGIN;
EXEC sys.sp_dropextendedproperty @name=N'@DatabaseName' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
END
GO
IF EXISTS (SELECT * FROM sys.fn_listextendedproperty(N'@LimitStoredProcLength' , N'SCHEMA',N'dbo', N'PROCEDURE',N'sp_doc', NULL,NULL))
BEGIN;
EXEC sys.sp_dropextendedproperty @name=N'@LimitStoredProcLength' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
END
GO
IF EXISTS (SELECT * FROM sys.fn_listextendedproperty(N'@Emojis' , N'SCHEMA',N'dbo', N'PROCEDURE',N'sp_doc', NULL,NULL))
BEGIN;
EXEC sys.sp_dropextendedproperty @name=N'@Emojis' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
END
GO
IF EXISTS (SELECT * FROM sys.fn_listextendedproperty(N'@Verbose' , N'SCHEMA',N'dbo', N'PROCEDURE',N'sp_doc', NULL,NULL))
BEGIN;
EXEC sys.sp_dropextendedproperty @name=N'@Verbose' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
END
GO
/***************************/
/* Create stored procedure */
/***************************/
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_doc]') AND [type] IN (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'CREATE PROCEDURE [dbo].[sp_doc] AS';
END
GO
ALTER PROCEDURE [dbo].[sp_doc]
@DatabaseName SYSNAME = NULL
,@ExtendedPropertyName SYSNAME = 'Description'
,@LimitStoredProcLength BIT = 1
,@Emojis BIT = 0
,@Verbose BIT = 1
/* Parameters defined here for testing only */
,@SqlMajorVersion TINYINT = 0
,@SqlMinorVersion SMALLINT = 0
WITH RECOMPILE
AS
/*
sp_doc - Always have current documentation by generating it on the fly in markdown.
Part of the DBA MultiTool http://dba-multitool.org
Version: 20201221
MIT License
Copyright (c) 2020 John McCall
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=========
Example:
EXEC sp_doc @DatabaseName = 'WideWorldImporters';
*/
BEGIN
SET NOCOUNT ON;
DECLARE @Sql NVARCHAR(MAX)
,@ParmDefinition NVARCHAR(500)
,@QuotedDatabaseName SYSNAME
,@Msg NVARCHAR(MAX)
,@SensitivityClassification BIT
-- Variables used for Emoji mode
,@Yes VARCHAR(20) = 'yes'
,@No VARCHAR(20) = 'no'
,@PK VARCHAR(20) = NULL
,@FK VARCHAR(20) = NULL
,@Column VARCHAR(20) = NULL;
-- Find Version
IF (@SqlMajorVersion = 0)
BEGIN;
SET @SqlMajorVersion = CAST(SERVERPROPERTY('ProductMajorVersion') AS TINYINT);
END;
IF (@SqlMinorVersion = 0)
BEGIN;
SET @SqlMinorVersion = CAST(SERVERPROPERTY('ProductMinorVersion') AS TINYINT);
END;
-- Validate Version
IF (@SqlMajorVersion < 11)
BEGIN;
SET @Msg = 'SQL Server versions below 2012 are not supported, sorry!';
RAISERROR(@Msg, 16, 1);
END;
-- Check database name
IF (@DatabaseName IS NULL)
BEGIN
SET @DatabaseName = DB_NAME();
IF (@Verbose = 1)
BEGIN;
SET @Msg = 'No database provided, assuming current database.';
RAISERROR(@Msg, 10, 1) WITH NOWAIT;
END;
END
ELSE IF (DB_ID(@DatabaseName) IS NULL)
BEGIN;
SET @Msg = 'Database not available.';
RAISERROR(@Msg, 16, 1);
END;
SET @QuotedDatabaseName = QUOTENAME(@DatabaseName); --Avoid injections
-- Check Emoji Mode
IF (@Emojis = 1)
BEGIN;
SET @Yes = ':heavy_check_mark:';
SET @No = ':x:';
SET @PK = ':key: ';
SET @FK = ':old_key: ';
SET @Column = ':page_facing_up: ';
END;
-- Check for Sensitivity Classifications
IF EXISTS (SELECT 1 FROM [sys].[system_views] WHERE [name] = 'sensitivity_classifications')
BEGIN
SET @Sql = N'USE ' + @QuotedDatabaseName + ';
IF EXISTS (SELECT 1 FROM [sys].[sensitivity_classifications])
BEGIN
SET @SensitivityClassification = 1;
END;
ELSE
BEGIN
SET @SensitivityClassification = 0;
END;';
SET @ParmDefinition = N'@SensitivityClassification BIT OUTPUT';
EXEC sp_executesql @Sql
,@ParmDefinition
,@SensitivityClassification OUTPUT;
END;
--Create table to hold EP data
SET @Sql = N'USE ' + @QuotedDatabaseName + ';
CREATE TABLE #markdown (
[id] INT IDENTITY(1,1),
[value] NVARCHAR(MAX));';
/******************************
Generate markdown for database
******************************/
--Database Name
SET @Sql = @Sql + N'
INSERT INTO #markdown (value)
VALUES (CONCAT(''# '', @DatabaseName) COLLATE DATABASE_DEFAULT);' +
--Database extended properties
+ N'INSERT INTO #markdown (value)
SELECT CONCAT(CHAR(13), CHAR(10), CAST([value] AS VARCHAR(8000)))
FROM [sys].[extended_properties] AS [ep]
WHERE [ep].[class] = 0
AND [ep].[name] = @ExtendedPropertyName;' +
--Database metadata
+ N'INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''| Property | Value |''))
,(''| --- | --- |'');
INSERT INTO #markdown
SELECT CONCAT(''| '', ''SQL Server Version'', '' | '', CAST(SERVERPROPERTY(''ProductVersion'') AS SYSNAME), '' |'')
UNION ALL
SELECT CONCAT(''| '', ''Compatibility Level'', '' | '', [compatibility_level], '' |'')
FROM [sys].[databases]
WHERE [name] = DB_NAME()
UNION ALL
SELECT CONCAT(''| '', ''Collation'', '' | '', [collation_name], '' |'')
FROM [sys].[databases]
WHERE [name] = DB_NAME();
' +
/****************************
Generate markdown for tables
****************************/
--Variables
+ N'DECLARE @objectid INT,
@indexobjectid INT,
@TrigObjectId INT,
@CheckConstObjectId INT,
@DefaultConstObjectId INT;
DECLARE @key_columns NVARCHAR(MAX),
@include_columns NVARCHAR(MAX);';
--Build table of contents
SET @Sql = @Sql + N'
IF EXISTS (SELECT 1 FROM [sys].[tables] WHERE [type] = ''U'' AND [is_ms_shipped] = 0)
BEGIN
INSERT INTO #markdown (value)
VALUES (''----'')
,(CONCAT(CHAR(13), CHAR(10), ''## Tables''))
,(CONCAT(CHAR(13), CHAR(10), ''Click to expand
'', CHAR(13), CHAR(10)));
' +
+ N'INSERT INTO #markdown (value)
SELECT CONCAT(''* ['', OBJECT_SCHEMA_NAME(object_id), ''.'', OBJECT_NAME(object_id), ''](#'', REPLACE(LOWER(OBJECT_SCHEMA_NAME(object_id)), '' '', ''-''), REPLACE(LOWER(OBJECT_NAME(object_id)), '' '', ''-''), '')'')
FROM [sys].[tables]
WHERE [type] = ''U''
AND [is_ms_shipped] = 0
ORDER BY OBJECT_SCHEMA_NAME([object_id]), [name] ASC;' +
--Object details
+ N'DECLARE obj_cursor CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT [object_id]
FROM [sys].[tables]
WHERE [type] = ''U''
AND [is_ms_shipped] = 0
ORDER BY OBJECT_SCHEMA_NAME([object_id]), [name] ASC;
OPEN obj_cursor
FETCH NEXT FROM obj_cursor INTO @objectid
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), ''### '', OBJECT_SCHEMA_NAME(@objectid), ''.'', OBJECT_NAME(@objectid));' +
--Extended Properties
+ N'INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), CAST([ep].[value] AS NVARCHAR(4000)))
FROM [sys].[tables] AS [t] WITH(NOLOCK)
INNER JOIN [sys].[extended_properties] AS [ep] WITH(NOLOCK) ON [t].[object_id] = [ep].[major_id]
WHERE [t].[object_id] = @objectid
AND [ep].[minor_id] = 0 --On the table
AND [ep].[name] = @ExtendedPropertyName;';
IF @SensitivityClassification = 1
BEGIN
SET @Sql = @Sql + N'
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''| Column | Type | Null | Foreign Key | Default | '', @ExtendedPropertyName COLLATE DATABASE_DEFAULT, '' | Classification |''))
,(''| --- | --- | --- | --- | --- | --- | --- |'');';
END
ELSE
BEGIN
SET @Sql = @Sql + N'
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''| Column | Type | Null | Foreign Key | Default | '', @ExtendedPropertyName COLLATE DATABASE_DEFAULT, '' |''))
,(''| --- | --- | --- | --- | --- | --- |'');';
END;
--Columns
SET @Sql = @Sql + N'
INSERT INTO #markdown
SELECT CONCAT(''| ''
,CASE
WHEN [ic].[object_id] IS NOT NULL
THEN CONCAT(@PK, ''**'',[c].[name],''**'')
WHEN [fk].[parent_object_id] IS NOT NULL
THEN CONCAT(@FK, [c].[name])
ELSE CONCAT(@Column, [c].[name])
END
,'' | ''
,CONCAT(UPPER(TYPE_NAME([user_type_id]))
,CASE
WHEN TYPE_NAME([user_type_id]) IN (N''decimal'',N''numeric'')
THEN CONCAT(N''('',CAST(precision AS varchar(5)), N'','',CAST(scale AS varchar(5)), N'')'')
WHEN TYPE_NAME([user_type_id]) IN (''varchar'', ''char'', ''varbinary'')
THEN CASE
WHEN [max_length] = -1
THEN N''(MAX)''
ELSE QUOTENAME(CAST([max_length] AS VARCHAR(10)), ''('')
END
WHEN TYPE_NAME([user_type_id]) IN (N''time'',N''datetime2'',N''datetimeoffset'')
THEN QUOTENAME(CAST(scale AS varchar(5)), ''('')
WHEN TYPE_NAME([user_type_id]) in (N''float'')
THEN CASE
WHEN [c].precision = 53
THEN N''''
ELSE CONCAT(N''('',CAST([c].precision AS varchar(5)),N'')'')
END
WHEN TYPE_NAME([c].user_type_id) IN (N''int'',N''bigint'',N''smallint'',N''tinyint'',N''money'',N''smallmoney'',
N''real'',N''datetime'',N''smalldatetime'',N''bit'',N''image'',N''text'',N''uniqueidentifier'',
N''date'',N''ntext'',N''sql_variant'',N''hierarchyid'',''geography'',N''timestamp'',N''xml'')
THEN N''''
WHEN TYPE_NAME([user_type_id]) IN (N''nvarchar'',N''nchar'', N''sysname'')
THEN CASE
WHEN [max_length] = -1
THEN N''(MAX)''
ELSE QUOTENAME(CAST([max_length]/2 AS VARCHAR(10)), ''('')
END
ELSE QUOTENAME(CAST([max_length] AS VARCHAR(10)), ''('')
END)
,'' | ''
,CASE [c].[is_nullable]
WHEN 1
THEN @Yes
ELSE @No
END
,'' | ''
,CASE
WHEN [fk].[parent_object_id] IS NOT NULL
THEN CONCAT(''['',QUOTENAME(OBJECT_SCHEMA_NAME([fk].[referenced_object_id])), ''.'', QUOTENAME(OBJECT_NAME([fk].[referenced_object_id])), ''.'', QUOTENAME(COL_NAME([fk].[referenced_object_id], [fk].[referenced_column_id])),'']'',''(#'',LOWER(OBJECT_SCHEMA_NAME([fk].[referenced_object_id])), LOWER(OBJECT_NAME([fk].[referenced_object_id])), '')'')
ELSE ''''
END
,'' | ''
,OBJECT_DEFINITION([dc].[object_id])
,'' | ''
,CAST([ep].[value] AS NVARCHAR(4000))
,'' |''';
IF @SensitivityClassification = 1
BEGIN
SET @Sql = @Sql + N'
,CASE
WHEN [sc].[label] IS NOT NULL
THEN CONCAT('' Label: '', CAST([sc].[Label] AS SYSNAME), ''
'', ''Type: '', CAST([sc].[Information_Type] AS SYSNAME), ''
'', ''Rank: '', CAST([Rank_Desc] AS SYSNAME), ''
'')
ELSE '' ''
END
,'' |''';
END
SET @Sql = @Sql + N')';
SET @Sql = @Sql + N'
FROM [sys].[tables] AS [t] WITH(NOLOCK)
INNER JOIN [sys].[columns] AS [c] WITH(NOLOCK) ON [t].[object_id] = [c].[object_id]
LEFT JOIN [sys].[extended_properties] AS [ep] WITH(NOLOCK) ON [t].[object_id] = [ep].[major_id]
AND [ep].[minor_id] > 0
AND [ep].[minor_id] = [c].[column_id]
AND [ep].[class] = 1 --Object/col
AND [ep].[name] = @ExtendedPropertyName
LEFT JOIN [sys].[foreign_key_columns] AS [fk] WITH(NOLOCK) ON [fk].[parent_object_id] = [c].[object_id]
AND [fk].[parent_column_id] = [c].[column_id]
LEFT JOIN [sys].[default_constraints] AS [dc] WITH(NOLOCK) ON [dc].[parent_object_id] = [c].[object_id]
AND [dc].[parent_column_id] = [c].[column_id]
LEFT JOIN [sys].[indexes] AS [pk] WITH(NOLOCK) ON [pk].[object_id] = [t].[object_id]
AND [pk].[is_primary_key] = 1
LEFT JOIN [sys].[index_columns] AS [ic] WITH(NOLOCK) ON [ic].[index_id] = [pk].[index_id]
AND [ic].[object_id] = [t].[object_id]
AND [ic].[column_id] = [c].[column_id]';
IF @SensitivityClassification = 1
BEGIN
SET @Sql = @Sql + N'
LEFT JOIN [sys].[sensitivity_classifications] AS [sc] WITH(NOLOCK) ON [sc].[major_id] = [t].[object_id]
AND [sc].[minor_id] = [c].[column_id]';
END;
SET @Sql = @Sql + N'
WHERE [t].[object_id] = @objectid;' +
--Indexes
+ N'IF EXISTS (SELECT 1 FROM [sys].[indexes] WHERE [object_id] = @objectid AND [type] > 0)
BEGIN
INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), ''#### '', ''Indexes'')
DECLARE [index_cursor] CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT [ind].[index_id]
FROM [sys].[indexes] AS [ind]
WHERE [ind].[object_id] = @objectId
AND [ind].[type] > 0 -- Not heap
ORDER BY [ind].[is_primary_key] DESC, [ind].[is_unique_constraint] DESC, [ind].[name] DESC
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''| Name | Type | Key Columns | Include Columns | '', @ExtendedPropertyName COLLATE DATABASE_DEFAULT, '' |''))
,(''| --- | --- | --- | --- | --- |'');
OPEN [index_cursor]
FETCH NEXT FROM [index_cursor] INTO @indexobjectid
WHILE @@FETCH_STATUS = 0
BEGIN
' +
-- Get key columns as a csv list
+ N'SELECT @key_columns = STUFF((
SELECT CONCAT('', '', QUOTENAME([col].[name]))
FROM [sys].[indexes] AS [ind]
INNER JOIN [sys].[index_columns] AS [ic] ON [ind].[object_id] = [ic].[object_id]
AND [ic].[index_id] = [ind].[index_id]
INNER JOIN [sys].[columns] AS [col] ON [ic].[object_id] = [col].[object_id]
AND [ic].[column_id] = [col].[column_id]
WHERE [ind].[object_id] = @objectid
AND [ind].[index_id] = @indexobjectid
AND [ic].[is_included_column] = 0
FOR XML PATH('''')
), 1, 2, ''''); ' +
-- Get included columns as a csv list
+ N'SELECT @include_columns = STUFF((
SELECT CONCAT('', '', QUOTENAME([col].[name]))
FROM [sys].[indexes] AS [ind]
INNER JOIN [sys].[index_columns] AS [ic] ON [ind].[object_id] = [ic].[object_id]
AND [ic].[index_id] = [ind].[index_id]
INNER JOIN [sys].[columns] AS [col] ON [ic].[object_id] = [col].[object_id]
AND [ic].[column_id] = [col].[column_id]
WHERE [ind].[object_id] = @objectid
AND [ind].[index_id] = @indexobjectid
AND [ic].[is_included_column] = 1
FOR XML PATH('''')
), 1, 2, '''');
INSERT INTO #markdown (value)
SELECT CONCAT(''| ''
,CASE
WHEN [ind].[is_primary_key] = 1
THEN CONCAT(@PK, ''**'',[ind].[name],''**'')
ELSE [ind].[name]
END
, '' | ''
, LOWER([ind].[type_desc]) COLLATE DATABASE_DEFAULT
, '' | ''
, @key_columns COLLATE DATABASE_DEFAULT
, '' | ''
, @include_columns COLLATE DATABASE_DEFAULT
, '' | ''
, CAST([ep].[value] AS NVARCHAR(4000)) COLLATE DATABASE_DEFAULT
, '' |'')
FROM [sys].[indexes] AS [ind]
LEFT JOIN [sys].[extended_properties] AS [ep] ON [ind].[object_id] = [ep].[major_id]
AND [ep].[minor_id] = [ind].[index_id]
AND [ep].[class] = 7 -- Index
AND [ep].[name] = @ExtendedPropertyName
WHERE [ind].[object_id] = @objectid
AND [ind].[index_id] = @indexobjectid;
FETCH NEXT FROM [index_cursor] INTO @indexobjectid;
END;
CLOSE [index_cursor];
DEALLOCATE [index_cursor];
END;
' +
--Triggers
+ N'IF EXISTS (SELECT * FROM [sys].[triggers] WHERE [parent_id] = @objectid)
BEGIN
INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), ''#### '', ''Triggers'')
DECLARE [trig_cursor] CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT [object_id]
FROM [sys].[triggers]
WHERE [parent_id] = @objectId
ORDER BY OBJECT_SCHEMA_NAME([object_id]), [name] ASC;
OPEN [trig_cursor]
FETCH NEXT FROM [trig_cursor] INTO @TrigObjectId
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO #markdown (value)
VALUES (CONCAT(''##### '', OBJECT_SCHEMA_NAME(@TrigObjectId), ''.'', OBJECT_NAME(@TrigObjectId)))
,(CONCAT(''###### '', ''Definition''))
,(CONCAT(''Click to expand
'', CHAR(13), CHAR(10)));' +
--Object definition
+ N'INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''```sql'',
CHAR(13), CHAR(10), OBJECT_DEFINITION(@CheckConstObjectId)))
,(''```'');
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''))
FETCH NEXT FROM [trig_cursor] INTO @TrigObjectId;
END;
CLOSE [trig_cursor];
DEALLOCATE [trig_cursor];
END;' +
--Check Constraints
+ N'IF EXISTS (SELECT 1 FROM [sys].[check_constraints] WHERE [parent_object_id] = @objectid)
BEGIN
INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), ''#### '', ''Check Constraints'')
DECLARE [check_cursor] CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT [object_id]
FROM [sys].[check_constraints]
WHERE [parent_object_id] = @objectid
ORDER BY OBJECT_SCHEMA_NAME(object_id), [name] ASC;
OPEN [check_cursor]
FETCH NEXT FROM [check_cursor] INTO @CheckConstObjectId
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10),''##### '', OBJECT_SCHEMA_NAME(@CheckConstObjectId), ''.'', OBJECT_NAME(@CheckConstObjectId)))
,(CONCAT(CHAR(13), CHAR(10),''###### '', ''Definition''))
,(CONCAT(CHAR(13), CHAR(10),''Click to expand
''));' +
--Object definition
+ N'INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''```sql'',
CHAR(13), CHAR(10), OBJECT_DEFINITION(@CheckConstObjectId)))
,(''```'');
INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''))
FETCH NEXT FROM [check_cursor] INTO @CheckConstObjectId;
END;
CLOSE [check_cursor];
DEALLOCATE [check_cursor];
END;' +
--Back to top
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), ''[Back to top](#'', LOWER(@DatabaseName COLLATE DATABASE_DEFAULT), '')''))
FETCH NEXT FROM obj_cursor INTO @objectid;
END;
CLOSE obj_cursor;
DEALLOCATE obj_cursor;' +
--End collapsible table section
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''));
END;'; --End markdown for tables
/***************************
Generate markdown for views
***************************/
--Build table of contents
SET @Sql = @Sql + N'
IF EXISTS (SELECT 1 FROM [sys].[views] WHERE [is_ms_shipped] = 0)
BEGIN;
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''## Views'')) ,(CONCAT(CHAR(13), CHAR(10), ''Click to expand
'', CHAR(13), CHAR(10)));
' +
+ N'INSERT INTO #markdown (value)
SELECT CONCAT(''* ['', OBJECT_SCHEMA_NAME(object_id), ''.'', OBJECT_NAME(object_id), ''](#'', REPLACE(LOWER(OBJECT_SCHEMA_NAME(object_id)), '' '', ''-''), REPLACE(LOWER(OBJECT_NAME(object_id)), '' '', ''-''), '')'')
FROM [sys].[views]
WHERE [is_ms_shipped] = 0
ORDER BY OBJECT_SCHEMA_NAME([object_id]), [name] ASC;' +
--Object details
+ N'DECLARE obj_cursor CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT [object_id]
FROM [sys].[views]
WHERE [is_ms_shipped] = 0
ORDER BY OBJECT_SCHEMA_NAME([object_id]), [name] ASC;
OPEN obj_cursor
FETCH NEXT FROM obj_cursor INTO @objectid
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), ''### '', OBJECT_SCHEMA_NAME(@objectid), ''.'', OBJECT_NAME(@objectid));' +
--Extended Properties
+ N'INSERT INTO #markdown
SELECT CAST([ep].[value] AS NVARCHAR(4000))
FROM [sys].[views] AS [v]
INNER JOIN [sys].[extended_properties] AS [ep] ON [v].[object_id] = [ep].[major_id]
WHERE [v].[object_id] = @objectid
AND [ep].[minor_id] = 0
AND [ep].[name] = @ExtendedPropertyName;
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''| Column | Type | Null | '', @ExtendedPropertyName COLLATE DATABASE_DEFAULT, '' |''))
,(''| --- | ---| --- | --- |'');' +
--Projected columns
+ N'INSERT INTO #markdown
SELECT CONCAT(''| '', [c].[name]
,'' | ''
,CONCAT(UPPER(TYPE_NAME([user_type_id]))
,CASE
WHEN TYPE_NAME([user_type_id]) IN (N''decimal'',N''numeric'')
THEN CONCAT(N''('',CAST(precision AS varchar(5)), N'','',CAST(scale AS varchar(5)), N'')'')
WHEN TYPE_NAME([user_type_id]) IN (''varchar'', ''char'', ''varbinary'')
THEN CASE
WHEN [max_length] = -1
THEN N''(MAX)''
ELSE QUOTENAME(CAST([max_length] AS VARCHAR(10)), ''('')
END
WHEN TYPE_NAME([user_type_id]) IN (N''time'',N''datetime2'',N''datetimeoffset'')
THEN QUOTENAME(CAST(scale AS varchar(5)), ''('')
WHEN TYPE_NAME([user_type_id]) in (N''float'')
THEN CASE
WHEN [c].precision = 53
THEN N''''
ELSE CONCAT(N''('',CAST([c].precision AS varchar(5)),N'')'')
END
WHEN TYPE_NAME([c].user_type_id) IN (N''int'',N''bigint'',N''smallint'',N''tinyint'',N''money'',N''smallmoney'',
N''real'',N''datetime'',N''smalldatetime'',N''bit'',N''image'',N''text'',N''uniqueidentifier'',
N''date'',N''ntext'',N''sql_variant'',N''hierarchyid'',''geography'',N''timestamp'',N''xml'')
THEN N''''
WHEN TYPE_NAME([user_type_id]) IN (N''nvarchar'',N''nchar'', N''sysname'')
THEN CASE
WHEN [max_length] = -1
THEN N''(MAX)''
ELSE QUOTENAME(CAST([max_length]/2 AS VARCHAR(10)), ''('')
END
ELSE QUOTENAME(CAST([max_length] AS VARCHAR(10)), ''('')
END)
,'' | ''
,CASE [c].[is_nullable]
WHEN 1
THEN @Yes
ELSE @No
END
,'' | ''
,CAST([ep].[value] AS NVARCHAR(4000))
,'' |'')
FROM [sys].[views] AS [o]
INNER JOIN [sys].[columns] AS [c] ON [o].[object_id] = [c].[object_id]
LEFT JOIN [sys].[extended_properties] AS [ep] ON [o].[object_id] = [ep].[major_id]
AND [ep].[minor_id] = [c].[column_id]
AND [ep].[name] = @ExtendedPropertyName
WHERE [o].[is_ms_shipped] = 0 -- User objects only
AND [o].[type] = ''V'' -- VIEW
AND [o].[object_id] = @objectid
ORDER BY SCHEMA_NAME([o].[schema_id]), [o].[type_desc], OBJECT_NAME([ep].major_id);
INSERT INTO #markdown (value)
VALUES(CONCAT(CHAR(13), CHAR(10), ''#### Definition''))
,(CONCAT(CHAR(13), CHAR(10), ''Click to expand
''));' +
--Object definition
+ N'INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''```sql'',
CHAR(13), CHAR(10), OBJECT_DEFINITION(@objectid)))
,(''```'');' +
--Indexes
+ N'IF EXISTS (SELECT 1 FROM [sys].[indexes] WHERE [object_id] = @objectid)
BEGIN
INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), ''#### '', ''Indexes'')
DECLARE [index_cursor] CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT [ind].[index_id]
FROM [sys].[indexes] AS [ind]
WHERE [ind].[object_id] = @objectId
AND [ind].[type] > 0 -- Not heap
ORDER BY [ind].[is_primary_key] DESC, [ind].[is_unique_constraint] DESC, [ind].[name] DESC
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''| Name | Type | Key Columns | Include Columns | '', @ExtendedPropertyName COLLATE DATABASE_DEFAULT, '' |''))
,(''| --- | --- | --- | --- | --- |'');
OPEN [index_cursor]
FETCH NEXT FROM [index_cursor] INTO @indexobjectid
WHILE @@FETCH_STATUS = 0
BEGIN
' +
-- Get key columns as a csv list
+ N'SELECT @key_columns = STUFF((
SELECT CONCAT('', '', QUOTENAME([col].[name]))
FROM [sys].[indexes] AS [ind]
INNER JOIN [sys].[index_columns] AS [ic] ON [ind].[object_id] = [ic].[object_id]
AND [ic].[index_id] = [ind].[index_id]
INNER JOIN [sys].[columns] AS [col] ON [ic].[object_id] = [col].[object_id]
AND [ic].[column_id] = [col].[column_id]
WHERE [ind].[object_id] = @objectid
AND [ind].[index_id] = @indexobjectid
AND [ic].[is_included_column] = 0
FOR XML PATH('''')
), 1, 2, ''''); ' +
-- Get included columns as a csv list
+ N'SELECT @include_columns = STUFF((
SELECT CONCAT('', '', QUOTENAME([col].[name]))
FROM [sys].[indexes] AS [ind]
INNER JOIN [sys].[index_columns] AS [ic] ON [ind].[object_id] = [ic].[object_id]
AND [ic].[index_id] = [ind].[index_id]
INNER JOIN [sys].[columns] AS [col] ON [ic].[object_id] = [col].[object_id]
AND [ic].[column_id] = [col].[column_id]
WHERE [ind].[object_id] = @objectid
AND [ind].[index_id] = @indexobjectid
AND [ic].[is_included_column] = 1
FOR XML PATH('''')
), 1, 2, '''');
INSERT INTO #markdown (value)
SELECT CONCAT(''| ''
,CASE
WHEN [ind].[is_primary_key] = 1
THEN CONCAT(@PK, ''**'',[ind].[name],''**'')
ELSE [ind].[name]
END
, '' | ''
, LOWER([ind].[type_desc]) COLLATE DATABASE_DEFAULT
, '' | ''
, @key_columns COLLATE DATABASE_DEFAULT
, '' | ''
, @include_columns COLLATE DATABASE_DEFAULT
, '' | ''
, CAST([ep].[value] AS NVARCHAR(4000)) COLLATE DATABASE_DEFAULT
, '' |'')
FROM [sys].[indexes] AS [ind]
LEFT JOIN [sys].[extended_properties] AS [ep] ON [ind].[object_id] = [ep].[major_id]
AND [ep].[minor_id] = [ind].[index_id]
AND [ep].[class] = 7 -- Index
AND [ep].[name] = @ExtendedPropertyName
WHERE [ind].[object_id] = @objectid
AND [ind].[index_id] = @indexobjectid;
FETCH NEXT FROM [index_cursor] INTO @indexobjectid;
END;
CLOSE [index_cursor];
DEALLOCATE [index_cursor];
END;
' +
--Back to top
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''))
,(CONCAT(CHAR(13), CHAR(10), ''[Back to top](#'', LOWER(@DatabaseName COLLATE DATABASE_DEFAULT), '')''));
FETCH NEXT FROM obj_cursor INTO @objectid;
END;
CLOSE obj_cursor;
DEALLOCATE obj_cursor;' +
--End collapsible view section
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''));
END;'; --End markdown for views
/**************************************
Generate markdown for stored procedures
**************************************/
--Build table of contents
SET @Sql = @Sql + N'
IF EXISTS (SELECT 1 FROM [sys].[procedures] WHERE [is_ms_shipped] = 0)
BEGIN;
INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), ''## Stored Procedures'')) ,(CONCAT(CHAR(13), CHAR(10), ''Click to expand
'', CHAR(13), CHAR(10)));
' +
+ N'INSERT INTO #markdown
SELECT CONCAT(''* ['', OBJECT_SCHEMA_NAME(object_id), ''.'', OBJECT_NAME(object_id), ''](#'', REPLACE(LOWER(OBJECT_SCHEMA_NAME(object_id)), '' '', ''-''), REPLACE(LOWER(OBJECT_NAME(object_id)), '' '', ''-''), '')'')
FROM [sys].[procedures]
WHERE [is_ms_shipped] = 0
ORDER BY OBJECT_SCHEMA_NAME(object_id), [name] ASC;' +
--Object details
+ N'DECLARE obj_cursor CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT [object_id]
FROM [sys].[procedures]
WHERE [is_ms_shipped] = 0
ORDER BY OBJECT_SCHEMA_NAME([object_id]), [name] ASC;
OPEN obj_cursor
FETCH NEXT FROM obj_cursor INTO @objectid
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), ''### '', OBJECT_SCHEMA_NAME(@objectid), ''.'', OBJECT_NAME(@objectid));' +
--Extended properties
+ N'INSERT INTO #markdown
SELECT CAST([ep].[value] AS NVARCHAR(4000))
FROM [sys].[procedures] AS [p]
INNER JOIN [sys].[extended_properties] AS [ep] ON [p].[object_id] = [ep].[major_id]
WHERE [p].[object_id] = @objectid
AND [ep].[minor_id] = 0
AND [ep].[name] = @ExtendedPropertyName;' +
--Check for parameters
+ N'IF EXISTS (SELECT * FROM [sys].[parameters] AS [param] WHERE [param].[object_id] = @objectid)
BEGIN
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''| Parameter | Type | Output | Description |''))
,(''| --- | --- | --- | --- |'');
INSERT INTO #markdown
select CONCAT(''| '', CASE WHEN LEN([param].[name]) = 0 THEN ''*Output*'' ELSE [param].[name] END
,'' | ''
,CONCAT(UPPER(TYPE_NAME([user_type_id]))
,CASE
WHEN TYPE_NAME([user_type_id]) IN (N''decimal'',N''numeric'')
THEN CONCAT(N''('',CAST(precision AS varchar(5)), N'','',CAST(scale AS varchar(5)), N'')'')
WHEN TYPE_NAME([user_type_id]) IN (''varchar'', ''char'', ''varbinary'')
THEN CASE
WHEN [max_length] = -1
THEN N''(MAX)''
ELSE QUOTENAME(CAST([max_length] AS VARCHAR(10)), ''('')
END
WHEN TYPE_NAME([user_type_id]) IN (N''time'',N''datetime2'',N''datetimeoffset'')
THEN QUOTENAME(CAST(scale AS varchar(5)), ''('')
WHEN TYPE_NAME([user_type_id]) in (N''float'')
THEN CASE
WHEN [param].precision = 53
THEN N''''
ELSE CONCAT(N''('',CAST([param].precision AS varchar(5)),N'')'')
END
WHEN TYPE_NAME([param].user_type_id) IN (N''int'',N''bigint'',N''smallint'',N''tinyint'',N''money'',N''smallmoney'',
N''real'',N''datetime'',N''smalldatetime'',N''bit'',N''image'',N''text'',N''uniqueidentifier'',
N''date'',N''ntext'',N''sql_variant'',N''hierarchyid'',''geography'',N''timestamp'',N''xml'') OR [is_readonly] = 1
THEN N''''
WHEN TYPE_NAME([user_type_id]) IN (N''nvarchar'',N''nchar'', N''sysname'')
THEN CASE
WHEN [max_length] = -1
THEN N''(MAX)''
ELSE QUOTENAME(CAST([max_length]/2 AS VARCHAR(10)), ''('')
END
ELSE QUOTENAME(CAST([max_length] AS VARCHAR(10)), ''('')
END)
,'' | ''
,CASE [is_output]
WHEN 1
THEN @Yes
ELSE @No
END
,'' | ''
,CAST([ep].[value] AS NVARCHAR(4000))
, '' |'')
FROM [sys].[procedures] AS [proc]
INNER JOIN [sys].[parameters] AS [param] ON [param].[object_id] = [proc].[object_id]
LEFT JOIN [sys].[extended_properties] AS [ep] ON [proc].[object_id] = [ep].[major_id]
AND [ep].[name] = [param].[name]
WHERE [proc].[object_id] = @objectid
ORDER BY [param].[parameter_id] ASC;
END
INSERT INTO #markdown (value)
VALUES(CONCAT(CHAR(13), CHAR(10), ''#### Definition''))
,(CONCAT(CHAR(13), CHAR(10), ''Click to expand
''));' +
--Object definition
+ N'
IF (@LimitStoredProcLength = 1 AND LEN(OBJECT_DEFINITION(@objectid)) > 8000)
BEGIN;
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''```sql'',
CHAR(13), CHAR(10), CAST(OBJECT_DEFINITION(@objectid) AS VARCHAR(8000))))
,(''/************************************************************************************************/'')
,(''/* sp_doc: Max 8000 characters reached. Set @LimitStoredProcLength = 0 to show full definition. */'')
,(''/************************************************************************************************/'')
,(''```'');
END;
ELSE
BEGIN;
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''```sql'',
CHAR(13), CHAR(10), OBJECT_DEFINITION(@objectid)))
,(''```'');
END;' +
--Back to top
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''))
,(CONCAT(CHAR(13), CHAR(10), ''[Back to top](#'', LOWER(@DatabaseName COLLATE DATABASE_DEFAULT), '')''));
FETCH NEXT FROM obj_cursor INTO @objectid
END;
CLOSE obj_cursor;
DEALLOCATE obj_cursor;' +
--End collapsible stored procedure section
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''));
END;'; --End markdown for stored procedures
/*************************************
Generate markdown for scalar functions
*************************************/
--Build table of contents
SET @Sql = @Sql + N'
IF EXISTS (SELECT 1 FROM [sys].[objects] WHERE [is_ms_shipped] = 0 AND [type] = ''FN'')
BEGIN;
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''## Scalar Functions'')) ,(CONCAT(CHAR(13), CHAR(10), ''Click to expand
'', CHAR(13), CHAR(10)));
' +
+ N'INSERT INTO #markdown
SELECT CONCAT(''* ['', OBJECT_SCHEMA_NAME(object_id), ''.'', OBJECT_NAME(object_id), ''](#'', REPLACE(LOWER(OBJECT_SCHEMA_NAME(object_id)), '' '', ''-''), REPLACE(LOWER(OBJECT_NAME(object_id)), '' '', ''-''), '')'')
FROM [sys].[objects]
WHERE [is_ms_shipped] = 0
AND [type] = ''FN'' --SQL_SCALAR_FUNCTION
ORDER BY OBJECT_SCHEMA_NAME([object_id]), [name] ASC;' +
--Object details
+ N'DECLARE obj_cursor CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT [object_id]
FROM [sys].[objects]
WHERE [is_ms_shipped] = 0
AND [type] = ''FN'' --SQL_SCALAR_FUNCTION
ORDER BY OBJECT_SCHEMA_NAME([object_id]), [name] ASC;
OPEN obj_cursor
FETCH NEXT FROM obj_cursor INTO @objectid
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), ''### '', OBJECT_SCHEMA_NAME(@objectid), ''.'', OBJECT_NAME(@objectid));' +
--Extended properties
+ N'INSERT INTO #markdown
SELECT CAST([ep].[value] AS NVARCHAR(4000))
FROM [sys].[objects] AS [o]
INNER JOIN [sys].[extended_properties] AS [ep] ON [o].[object_id] = [ep].[major_id]
WHERE [o].[object_id] = @objectid
AND [ep].[minor_id] = 0
AND [ep].[name] = @ExtendedPropertyName;' +
--Check for parameters
+ N'IF EXISTS (SELECT * FROM [sys].[parameters] AS [param] WHERE [param].[object_id] = @objectid)
BEGIN
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''| Parameter | Type | Output | Description |''))
,(''| --- | --- | --- | --- |'');
INSERT INTO #markdown
select CONCAT(''| '', CASE WHEN LEN([param].[name]) = 0 THEN ''*Output*'' ELSE [param].[name] END
,'' | ''
,CONCAT(UPPER(TYPE_NAME([user_type_id]))
,CASE
WHEN TYPE_NAME([user_type_id]) IN (N''decimal'',N''numeric'')
THEN CONCAT(N''('',CAST(precision AS varchar(5)), N'','',CAST(scale AS varchar(5)), N'')'')
WHEN TYPE_NAME([user_type_id]) IN (''varchar'', ''char'', ''varbinary'')
THEN CASE
WHEN [max_length] = -1
THEN N''(MAX)''
ELSE QUOTENAME(CAST([max_length] AS VARCHAR(10)), ''('')
END
WHEN TYPE_NAME([user_type_id]) IN (N''time'',N''datetime2'',N''datetimeoffset'')
THEN QUOTENAME(CAST(scale AS varchar(5)), ''('')
WHEN TYPE_NAME([user_type_id]) in (N''float'')
THEN CASE
WHEN [param].precision = 53
THEN N''''
ELSE CONCAT(N''('',CAST([param].precision AS varchar(5)),N'')'')
END
WHEN TYPE_NAME([param].user_type_id) IN (N''int'',N''bigint'',N''smallint'',N''tinyint'',N''money'',N''smallmoney'',
N''real'',N''datetime'',N''smalldatetime'',N''bit'',N''image'',N''text'',N''uniqueidentifier'',
N''date'',N''ntext'',N''sql_variant'',N''hierarchyid'',''geography'',N''timestamp'',N''xml'') OR [is_readonly] = 1
THEN N''''
WHEN TYPE_NAME([user_type_id]) IN (N''nvarchar'',N''nchar'', N''sysname'')
THEN CASE
WHEN [max_length] = -1
THEN N''(MAX)''
ELSE QUOTENAME(CAST([max_length]/2 AS VARCHAR(10)), ''('')
END
ELSE QUOTENAME(CAST([max_length] AS VARCHAR(10)), ''('')
END)
,'' | ''
,CASE [is_output]
WHEN 1
THEN @Yes
ELSE @No
END
,'' | ''
,CAST([ep].[value] AS NVARCHAR(4000))
, '' |'')
FROM [sys].[objects] AS [o]
INNER JOIN [sys].[parameters] AS [param] ON [param].[object_id] = [o].[object_id]
LEFT JOIN [sys].[extended_properties] AS [ep] ON [o].[object_id] = [ep].[major_id]
AND [ep].[name] = [param].[name]
WHERE [o].[object_id] = @objectid
ORDER BY [param].[parameter_id] ASC;
END;
INSERT INTO #markdown (value)
VALUES(CONCAT(CHAR(13), CHAR(10), ''#### Definition''))
,(CONCAT(CHAR(13), CHAR(10), ''Click to expand
''));' +
--Object definition
+ N'INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''```sql'',
CHAR(13), CHAR(10), OBJECT_DEFINITION(@objectid)))
,(''```'');' +
--Back to top
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''))
,(CONCAT(CHAR(13), CHAR(10), ''[Back to top](#'', LOWER(@DatabaseName COLLATE DATABASE_DEFAULT), '')''));
FETCH NEXT FROM obj_cursor INTO @objectid;
END;
CLOSE obj_cursor;
DEALLOCATE obj_cursor;' +
--End collapsible scalar functions section
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''));
END;'; --End markdown for scalar functions
/************************************
Generate markdown for table functions
************************************/
--Build table of contents
SET @Sql = @Sql + N'
IF EXISTS (SELECT 1 FROM [sys].[objects] WHERE [is_ms_shipped] = 0 AND [type] = ''IF'')
BEGIN;
INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), ''## Table Functions'')) ,(CONCAT(CHAR(13), CHAR(10), ''Click to expand
'', CHAR(13), CHAR(10)));
' +
+ N'INSERT INTO #markdown
SELECT CONCAT(''* ['', OBJECT_SCHEMA_NAME(object_id), ''.'', OBJECT_NAME(object_id), ''](#'', REPLACE(LOWER(OBJECT_SCHEMA_NAME(object_id)), '' '', ''-''), REPLACE(LOWER(OBJECT_NAME(object_id)), '' '', ''-''), '')'')
FROM [sys].[objects]
WHERE [is_ms_shipped] = 0
AND [type] = ''IF'' --SQL_INLINE_TABLE_VALUED_FUNCTION
ORDER BY OBJECT_SCHEMA_NAME(object_id), [name] ASC;' +
--Object details
+ N'DECLARE obj_cursor CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT [object_id]
FROM [sys].[objects]
WHERE [is_ms_shipped] = 0
AND [type] = ''IF'' --SQL_INLINE_TABLE_VALUED_FUNCTION
ORDER BY OBJECT_SCHEMA_NAME([object_id]), [name] ASC;
OPEN obj_cursor
FETCH NEXT FROM obj_cursor INTO @objectid
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), ''### '', OBJECT_SCHEMA_NAME(@objectid), ''.'', OBJECT_NAME(@objectid));' +
--Extended properties
+ N'INSERT INTO #markdown
SELECT CAST([ep].[value] AS NVARCHAR(4000))
FROM [sys].[objects] AS [o]
INNER JOIN [sys].[extended_properties] AS [ep] ON [o].[object_id] = [ep].[major_id]
WHERE [o].[object_id] = @objectid
AND [ep].[minor_id] = 0
AND [ep].[name] = @ExtendedPropertyName;' +
--Check for parameters
+ N'IF EXISTS (SELECT * FROM [sys].[parameters] AS [param] WHERE [param].[object_id] = @objectid)
BEGIN
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''| Parameter | Type | Output | Description |''))
,(''| --- | --- | --- | --- |'');
INSERT INTO #markdown
select CONCAT(''| '', CASE WHEN LEN([param].[name]) = 0 THEN ''*Output*'' ELSE [param].[name] END
,'' | ''
,CONCAT(UPPER(TYPE_NAME([user_type_id]))
,CASE
WHEN TYPE_NAME([user_type_id]) IN (N''decimal'',N''numeric'')
THEN CONCAT(N''('',CAST(precision AS varchar(5)), N'','',CAST(scale AS varchar(5)), N'')'')
WHEN TYPE_NAME([user_type_id]) IN (''varchar'', ''char'')
THEN QUOTENAME(CAST([max_length] AS VARCHAR(10)), ''('')
WHEN TYPE_NAME([user_type_id]) IN (N''time'',N''datetime2'',N''datetimeoffset'')
THEN QUOTENAME(CAST(scale AS varchar(5)), ''('')
WHEN TYPE_NAME([user_type_id]) in (N''float'')
THEN CASE
WHEN precision = 53
THEN N''''
ELSE QUOTENAME(CAST(precision AS varchar(5)),''('') END
WHEN TYPE_NAME([user_type_id]) IN (N''int'',N''bigint'',N''smallint'',N''tinyint'',N''money'',N''smallmoney'',
N''real'',N''datetime'',N''smalldatetime'',N''bit'',N''image'',N''text'',N''uniqueidentifier'',
N''date'',N''ntext'',N''sql_variant'',N''hierarchyid'',''geography'',N''timestamp'',N''xml'')
THEN N''''
ELSE CASE
WHEN [is_readonly] = 1 --User defined table type
THEN N''''
WHEN [max_length] = -1
THEN N''(MAX)''
WHEN TYPE_NAME([user_type_id]) IN (N''nvarchar'',N''nchar'', N''sysname'')
THEN QUOTENAME(CAST([max_length]/2 AS VARCHAR(10)), ''('')
ELSE QUOTENAME(CAST([max_length] AS VARCHAR(10)), ''('')
END
END)
,'' | ''
,CASE [is_output]
WHEN 1
THEN @Yes
ELSE @No
END
,'' | ''
,CAST([ep].[value] AS NVARCHAR(4000))
, '' |'')
FROM [sys].[objects] AS [o]
INNER JOIN [sys].[parameters] AS [param] ON [param].[object_id] = [o].[object_id]
LEFT JOIN [sys].[extended_properties] AS [ep] ON [o].[object_id] = [ep].[major_id]
AND [ep].[name] = [param].[name]
WHERE [o].[object_id] = @objectid
ORDER BY [param].[parameter_id] ASC;
END;
INSERT INTO #markdown (value)
VALUES(CONCAT(CHAR(13), CHAR(10), ''#### Definition''))
,(CONCAT(CHAR(13), CHAR(10), ''Click to expand
''));' +
--Object definition
+ N'INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''```sql'',
CHAR(13), CHAR(10), OBJECT_DEFINITION(@objectid)))
,(''```'');' +
--Back to top
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''))
,(CONCAT(CHAR(13), CHAR(10),''[Back to top](#'', LOWER(@DatabaseName COLLATE DATABASE_DEFAULT), '')''));
FETCH NEXT FROM obj_cursor INTO @objectid;
END;
CLOSE obj_cursor;
DEALLOCATE obj_cursor;' +
--End collapsible table functions section
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''));
END;'; --End markdown for table functions
/******************************
Generate markdown for synonyms
******************************/
--Build table of contents
SET @Sql = @Sql + N'
IF EXISTS (SELECT 1 FROM [sys].[synonyms] WHERE [is_ms_shipped] = 0)
BEGIN;
INSERT INTO #markdown ([value])
VALUES (CONCAT(CHAR(13), CHAR(10), ''## Synonyms'')) ,(CONCAT(CHAR(13), CHAR(10), ''Click to expand
''));
' +
+ N'INSERT INTO #markdown
SELECT CONCAT(''* ['', OBJECT_SCHEMA_NAME(object_id), ''.'', OBJECT_NAME(object_id), ''](#'', REPLACE(LOWER(OBJECT_SCHEMA_NAME(object_id)), '' '', ''-''), REPLACE(LOWER(OBJECT_NAME(object_id)), '' '', ''-''), '')'')
FROM [sys].[synonyms]
WHERE [is_ms_shipped] = 0
ORDER BY OBJECT_SCHEMA_NAME([object_id]), [name] ASC;' +
--Object details
+ N'DECLARE obj_cursor CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT [object_id]
FROM [sys].[synonyms]
WHERE [is_ms_shipped] = 0
ORDER BY OBJECT_SCHEMA_NAME([object_id]), [name] ASC;
OPEN obj_cursor
FETCH NEXT FROM obj_cursor INTO @objectid
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), ''### '', OBJECT_SCHEMA_NAME(@objectid), ''.'', OBJECT_NAME(@objectid), CHAR(13), CHAR(10)); ' +
--Extended properties
+ N'INSERT INTO #markdown
SELECT CAST([ep].[value] AS NVARCHAR(4000))
FROM [sys].[synonyms] AS [s]
INNER JOIN [sys].[extended_properties] AS [ep] ON [s].[object_id] = [ep].[major_id]
WHERE [s].[object_id] = @objectid
AND [ep].[minor_id] = 0
AND [ep].[name] = @ExtendedPropertyName;
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''| Synonym | Base Object |''))
,(''| --- | --- |'');' +
--Object mapping
+ N'INSERT INTO #markdown
SELECT CONCAT(''| '', OBJECT_SCHEMA_NAME([syn].[object_id]), ''.'', OBJECT_NAME([syn].[object_id])
,'' | ''
,CASE WHEN PARSENAME([base_object_name], 3) = DB_NAME()
THEN CONCAT(''['', PARSENAME([base_object_name], 3), ''.'', PARSENAME([base_object_name], 2), ''.'', PARSENAME([base_object_name], 1), '']'', ''(#'', PARSENAME([base_object_name], 2), ''.'', PARSENAME([base_object_name], 1), '')'')
ELSE CONCAT(PARSENAME([base_object_name], 3), PARSENAME([base_object_name], 2), PARSENAME([base_object_name], 1))
END
,'' |'')
FROM [sys].[synonyms] AS [syn]
WHERE [syn].[object_id] = @objectid;' +
--Back to top
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10),''[Back to top](#'', LOWER(@DatabaseName COLLATE DATABASE_DEFAULT), '')''));
FETCH NEXT FROM obj_cursor INTO @objectid
END
CLOSE obj_cursor
DEALLOCATE obj_cursor;' +
--End collapsible synonyms section
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''));
END;'; --End markdown for synonyms
/***********************************************
Generate markdown for user defined table types
***********************************************/
--Build table of contents
SET @Sql = @Sql + N'
IF EXISTS (SELECT 1 FROM [sys].[table_types] WHERE [is_user_defined] = 1)
BEGIN
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''## User Defined Table Types''))
,(CONCAT(CHAR(13), CHAR(10), ''Click to expand
'', CHAR(13), CHAR(10)));
' +
+ N'INSERT INTO #markdown (value)
SELECT CONCAT(''* ['', SCHEMA_NAME([schema_id]), ''.'', [name], ''](#'', REPLACE(LOWER(SCHEMA_NAME([schema_id])), '' '', ''-''), REPLACE(LOWER([name]), '' '', ''-''), '')'')
FROM [sys].[table_types]
WHERE [is_user_defined] = 1
ORDER BY OBJECT_SCHEMA_NAME([type_table_object_id]), [name] ASC;' +
--Object details
+ N'DECLARE obj_cursor CURSOR
LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
SELECT [type_table_object_id]
FROM [sys].[table_types]
WHERE [is_user_defined] = 1
ORDER BY OBJECT_SCHEMA_NAME([type_table_object_id]), [name] ASC;
OPEN obj_cursor
FETCH NEXT FROM obj_cursor INTO @objectid
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), ''### '', SCHEMA_NAME([schema_id]), ''.'', [name])
FROM [sys].[table_types]
WHERE [type_table_object_id] = @objectid
AND [is_user_defined] = 1;' +
--Extended Properties
+ N'INSERT INTO #markdown
SELECT CONCAT(CHAR(13), CHAR(10), CAST([ep].[value] AS NVARCHAR(4000)))
FROM [sys].[table_types] AS [tt]
INNER JOIN [sys].[extended_properties] AS [ep] ON [tt].[type_table_object_id] = [ep].[major_id]
WHERE [tt].[type_table_object_id] = @objectid
AND [ep].[minor_id] = 0 --On the table
AND [ep].[name] = @ExtendedPropertyName
AND [tt].[is_user_defined] = 1;
INSERT INTO #markdown (value)
VALUES (CONCAT(CHAR(13), CHAR(10), ''| Column | Type | Null | Default | '', @ExtendedPropertyName COLLATE DATABASE_DEFAULT, '' |''))
,(''| --- | ---| --- | --- | --- |'');' +
--Columns
+ N'INSERT INTO #markdown
SELECT CONCAT(''| ''
,CASE
WHEN [ic].[object_id] IS NOT NULL
THEN ISNULL(CONCAT(''**'',[c].[name],''**''), ''N/A'')
ELSE ISNULL([c].[name], ''N/A'')
END
,'' | ''
,CONCAT(UPPER(TYPE_NAME([c].[user_type_id]))
,CASE
WHEN TYPE_NAME([c].[user_type_id]) IN (N''decimal'',N''numeric'')
THEN CONCAT(N''('',CAST([c].[precision] AS VARCHAR(5)), N'','',CAST([c].[scale] AS varchar(5)), N'')'')
WHEN TYPE_NAME([c].[user_type_id]) IN (''varchar'', ''char'', ''varbinary'')
THEN CASE
WHEN [c].[max_length] = -1
THEN N''(MAX)''
ELSE QUOTENAME(CAST([c].[max_length] AS VARCHAR(10)), ''('')
END
WHEN TYPE_NAME([c].[user_type_id]) IN (N''time'',N''datetime2'',N''datetimeoffset'')
THEN QUOTENAME(CAST([c].[scale] AS VARCHAR(5)), ''('')
WHEN TYPE_NAME([c].[user_type_id]) in (N''float'')
THEN CASE
WHEN [c].precision = 53
THEN N''''
ELSE CONCAT(N''('',CAST([c].[precision] AS VARCHAR(5)),N'')'')
END
WHEN TYPE_NAME([c].[user_type_id]) IN (N''int'',N''bigint'',N''smallint'',N''tinyint'',N''money'',N''smallmoney'',
N''real'',N''datetime'',N''smalldatetime'',N''bit'',N''image'',N''text'',N''uniqueidentifier'',
N''date'',N''ntext'',N''sql_variant'',N''hierarchyid'',''geography'',N''timestamp'',N''xml'')
THEN N''''
WHEN TYPE_NAME([c].[user_type_id]) IN (N''nvarchar'',N''nchar'', N''sysname'')
THEN CASE
WHEN [c].[max_length] = -1
THEN N''(MAX)''
ELSE QUOTENAME(CAST([c].[max_length]/2 AS VARCHAR(10)), ''('')
END
ELSE QUOTENAME(CAST([c].[max_length] AS VARCHAR(10)), ''('')
END)
,'' | ''
,CASE [c].[is_nullable]
WHEN 1
THEN @Yes
ELSE @No
END
,'' | ''
,OBJECT_DEFINITION([dc].[object_id])
,'' | ''
,CAST([ep].[value] AS NVARCHAR(4000))
,'' |'')
FROM [sys].[table_types] AS [tt]
INNER JOIN [sys].[columns] AS [c] ON [tt].[type_table_object_id] = [c].[object_id]
LEFT JOIN [sys].[extended_properties] AS [ep] ON [tt].[type_table_object_id] = [ep].[major_id]
AND [ep].[minor_id] > 0
AND [ep].[minor_id] = [c].[column_id]
AND [ep].[class] = 1 --Object/col
-- AND [ep].[name] = @ExtendedPropertyName
LEFT JOIN [sys].[foreign_key_columns] AS [fk] ON [fk].[parent_object_id] = [c].[object_id]
AND [fk].[parent_column_id] = [c].[column_id]
LEFT JOIN [sys].[default_constraints] [dc] ON [dc].[parent_object_id] = [c].[object_id]
AND [dc].[parent_column_id] = [c].[column_id]
LEFT JOIN [sys].[indexes] AS [pk] ON [pk].[object_id] = [tt].[type_table_object_id]
AND [pk].[is_primary_key] = 1
LEFT JOIN [sys].[index_columns] AS [ic] ON [ic].[index_id] = [pk].[index_id]
AND [ic].[object_id] = [tt].[type_table_object_id]
AND [ic].[column_id] = [c].[column_id]
WHERE [tt].[type_table_object_id] = @objectid
AND [tt].[is_user_defined] = 1;' +
--Back to top
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), ''[Back to top](#'', LOWER(@DatabaseName COLLATE DATABASE_DEFAULT), '')''))
FETCH NEXT FROM obj_cursor INTO @objectid;
END;
CLOSE obj_cursor;
DEALLOCATE obj_cursor;' +
--End collapsible table section
+ N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), '' ''));
END;'; --End markdown for user defined table types
--Attribution
SET @Sql = @Sql + N'INSERT INTO #markdown
VALUES (CONCAT(CHAR(13), CHAR(10), ''----''))
,(CONCAT(CHAR(13), CHAR(10), ''*Markdown generated by [sp_doc](http://dba-multitool.org)''))
,(CONCAT('' at '', SYSDATETIMEOFFSET(), ''.*''));';
--Return all data
SET @Sql = @Sql + N'
SELECT [value]
FROM #markdown
ORDER BY [ID] ASC;';
SET @ParmDefinition = N'@ExtendedPropertyName SYSNAME, @DatabaseName SYSNAME, @LimitStoredProcLength BIT, @Yes VARCHAR(20), @No VARCHAR(20), @PK VARCHAR(20), @FK VARCHAR(20), @Column VARCHAR(20)';
EXEC sp_executesql @Sql
,@ParmDefinition
,@ExtendedPropertyName
,@DatabaseName
,@LimitStoredProcLength
,@Yes
,@No
,@PK
,@FK
,@Column;
END;
GO
EXEC sys.sp_addextendedproperty @name=N'Description', @value=N'Generate on the fly database documentation in markdown. Documentation at https://expresssql.lowlydba.com' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
GO
EXEC sys.sp_addextendedproperty @name=N'@DatabaseName', @value=N'Target database to document. Default is the stored procedure''s database.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
GO
EXEC sys.sp_addextendedproperty @name=N'@ExtendedPropertyName', @value=N'Key for extended properties on objects. Default is ''Description''.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
GO
EXEC sys.sp_addextendedproperty @name=N'@SqlMajorVersion', @value=N'Used for unit testing purposes only.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
GO
EXEC sys.sp_addextendedproperty @name=N'@SqlMinorVersion', @value=N'Used for unit testing purposes only.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
GO
EXEC sys.sp_addextendedproperty @name=N'@LimitStoredProcLength', @value=N'Limit stored procedure contents to 8000 characters, to avoid memory issues with some IDEs. Default is 1.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
GO
EXEC sys.sp_addextendedproperty @name=N'@Emojis', @value=N'Use emojis when generating documentation. Default is 0.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
GO
EXEC sys.sp_addextendedproperty @name=N'@Verbose', @value=N'Whether or not to print additional information during the script run. Default is 0.' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'PROCEDURE',@level1name=N'sp_doc';
GO