SET ANSI_NULLS ON; SET QUOTED_IDENTIFIER ON; SET CONCAT_NULL_YIELDS_NULL ON; GO DECLARE @DBname NVARCHAR(128) = DB_NAME(); RAISERROR('/******************************************',0,0,@DBname) WITH NOWAIT; --Check for the Inspector Schema in the reporting database and create the schema if it does not exist IF SCHEMA_ID('Inspector') IS NULL BEGIN RAISERROR('Creating Inspector Schema in database: %s',0,0,@DBname) WITH NOWAIT; EXEC sp_executesql N'CREATE SCHEMA [Inspector]' END GO DECLARE @DBname NVARCHAR(128) = DB_NAME(); RAISERROR('Creating Inspector setup stored procedure in database: %s',0,0,@DBname) WITH NOWAIT; RAISERROR('******************************************/',0,0,@DBname) WITH NOWAIT; IF OBJECT_ID('Inspector.InspectorSetup') IS NOT NULL DROP PROCEDURE [Inspector].[InspectorSetup]; GO /* @ ,@ #@@ @@@ @@@@@@@@@; @@@@@@@@@@ :@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@; @@@@@@@@@@@@ @@@@@@@@@@@@ `+@@@@@@@@@@+ .@@` #@, .@@@@@@@@@@@@@@@@@@@@@@@@: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@ @@ @@ #@ @ @ #@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@ @@@@ @# #@ @ @ ;@@@@@@@@@@@@@@@@@@@@@@@@@@' @ @ @# @# #@ @ #@@@ @@@@ @@@ @@@@@ @@@ @@ @ @ @@ @ @@ .+@@@@@@@@@@@@@@@@+. @@@@ @ @@ @# #@ @ #@ @ @@ @ @ @ @@ @ @ @ `@ @ @ @ @ @@ '` `,# @@@@ @ @@ @# #@ @ #@ @ @# @ @@@@@ @ @ @ @ @ @ @@@@ @` ,@@@@ '@@@@@@@@@@@@@ .@@@@; @ @ @@ @# #@ @ #@ @ @@ @ @@ @ ` @ @ @ @@@ @ @ #@@@@@@ @@@@@ +@@@@ +@@@@@@ @@@@ @@@@ @@@@@ `@@@@@ #@ @ #@ @@ @ @ @ @@ @ @ @ @ @ @ @ @@@@@@@@ ,#. `#; @@@@@@@@' @@ @@ @@@@@ @@, #@ @ @@ @ @@ @@ #@ @@ @ @@ @ ;#@@@@@@@@ @@@@@@@@@#, @ ,@@@@+ @@@@@+` .@@` `@@@@ © www.sqlundercover.com +@@@@ @@@@@+ @@@@@@@ @@@@@@@@# @@@@@@@ @@@@@@, :@@@@@' ;@@@@` `@@@@ @@@+ @#:@@ @@ @` # Author: Adrian Buckman Created Date: 15/07/2017 Revision date: 06/12/2021 Version: 2.7 Description: SQLUndercover Inspector setup script Case sensitive compatible. Creates [Inspector].[InspectorSetup] stored procedure. URL: https://github.com/SQLUndercover/UndercoverToolbox/blob/master/SQLUndercoverInspector/SQLUndercoverinspectorV2.sql User guide: https://sqlundercover.com/inspectoruserguide/ © www.sqlundercover.com MIT License ------------ Copyright 2021 Sql Undercover 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. */ CREATE PROCEDURE [Inspector].[InspectorSetup] ( @Databasename NVARCHAR(128) = NULL, --Name of the Logging Database @DataDrive VARCHAR(50) = NULL, --List Data Drives here (Comma delimited e.g 'P,Q,R,S') @LogDrive VARCHAR(50)= NULL, --List Log Drives here (Comma delimited e.g 'T,U,V,W') @StackNameForEmailSubject VARCHAR(255) = 'SQLUndercover', --Specify the name for this stack that you want to show in the email subject @EmailRecipientList VARCHAR(1000) = NULL, -- This will populate the EmailRecipients table for 'DBA' @BackupsPath VARCHAR(255) = NULL, -- Backup Drive and path @LinkedServername NVARCHAR(128) = N'DEPRECATED', -- No longer in use, left here so we didnt break the auto update feature without the need of replacing the file. @DriveSpaceHistoryRetentionInDays INT = 90, -- Days to retain drive space information @DaysUntilDriveFullThreshold TINYINT = 56, -- Estimated days until drive is full - Specify the threshold for when you will start to receive alerts (Red highlight and Alert header entry) @FreeSpaceRemainingPercent TINYINT = 10,-- Specify the percentage of drive space remaining where you want to start seeing a yellow highlight against the drive @DriveLetterExcludes VARCHAR(10) = NULL, -- Exclude Drive letters from showing Yellow Advisory warnings when @FreeSpaceRemainingPercent has been reached/exceeded e.g C,D (Comma Delimited) @DatabaseGrowthsAllowedPerDay TINYINT = 1, -- Total Database Growths acceptable for a 24hour period If exceeded a Yellow Advisory condition will be shown @MAXDatabaseGrowthsAllowedPerDay TINYINT = 10, -- MAX Database Growths for a 24 hour period If equal or exceeded a Red Warning condition will be shown @AgentJobOwnerExclusions VARCHAR(255) = 'sa', --Exclude agent jobs with these owners (Comma delimited) @FullBackupThreshold TINYINT = 8, -- X Days older than Getdate() @DiffBackupThreshold TINYINT = 24, -- X Hours older than Getdate() @LogBackupThreshold TINYINT = 20, -- X Minutes older than Getdate() @DatabaseOwnerExclusions VARCHAR(255) = 'sa', --Exclude databases with these owners (Comma delimited) @LongRunningTransactionThreshold INT = 300, -- Threshold in seconds, display running transactions that exceed this duration during collection @StartTime TIME(0) = '08:55', --Set the start of the time window you want collections to run for the Default ModuleConfig @EndTime TIME(0) = '17:30', --Set the end of the time window you want collections to run for the Default ModuleConfig @InitialSetup BIT = 0, --Set to 1 for intial setup, 0 to Upgrade or re deploy to preserve previously logged data and settings config. @EnableAgentJob BIT = 1, @Help BIT = 0 --Show example Setup command ) AS BEGIN SET ANSI_NULLS ON; SET QUOTED_IDENTIFIER ON; SET CONCAT_NULL_YIELDS_NULL ON; DECLARE @Revisiondate DATE = '20211206'; DECLARE @Build VARCHAR(6) ='2.7' DECLARE @JobID UNIQUEIDENTIFIER; DECLARE @JobsWithoutSchedules VARCHAR(1000); --Allowing NULL values but only when @Help = 1 otherwise raise an error stating that values need to be specified. IF ((@Databasename IS NULL OR @DataDrive IS NULL OR @LogDrive IS NULL) AND @Help = 0) BEGIN RAISERROR('@Databasename, @DataDrive, @LogDrive, @BackupsPath cannot be NULL when @Help = 0 is specified',11,0) WITH NOWAIT; RETURN; END IF (DB_ID(@Databasename) IS NULL AND @Help = 0) BEGIN RAISERROR('Please enter a valid database name',11,0) WITH NOWAIT; RETURN; END IF @Help = 1 BEGIN PRINT ' --Inspector V'+@Build+' --Revision date: '+CONVERT(VARCHAR(17),@Revisiondate,113)+' --You specified @Help = 1 - No setup has been carried out , here is an example command: EXEC [Inspector].[InspectorSetup] --Required Parameters (No defaults) @Databasename = '''+DB_NAME()+''', @DataDrive = '''+ISNULL(@DataDrive,'S,T')+''', @LogDrive = '''+ISNULL(@LogDrive,'U,V')+''', --Optional Parameters (Defaults Specified), ignored when @InitialSetup = 0 @BackupsPath = ''F:\Backups\'', @StackNameForEmailSubject = ''SQLUndercover'', @EmailRecipientList = NULL, @DriveSpaceHistoryRetentionInDays = 90, @DaysUntilDriveFullThreshold = 56, @FreeSpaceRemainingPercent = 10, @DriveLetterExcludes = NULL, @DatabaseGrowthsAllowedPerDay = 1, @MAXDatabaseGrowthsAllowedPerDay = 10, @AgentJobOwnerExclusions = ''sa'', @FullBackupThreshold = 8, @DiffBackupThreshold = 24, @LogBackupThreshold = 20, @DatabaseOwnerExclusions = ''sa'', @StartTime = ''08:55'', @EndTime = ''17:30'', @LongRunningTransactionThreshold = 300, @InitialSetup = 0, @EnableAgentJob = 1, @Help = 0; ' RETURN; END IF @InitialSetup IS NULL BEGIN RAISERROR('@InitialSetup cannot be NULL , please specify 0 or 1',11,0) WITH NOWAIT; RETURN; END DECLARE @Compatibility BIT --SET compatibility to 1 if server version includes STRING_SPLIT SELECT @Compatibility = CASE WHEN SERVERPROPERTY ('productversion') >= '13.0.4001.0' AND compatibility_level >= 130 THEN 1 ELSE 0 END FROM sys.databases WHERE name = DB_NAME(); IF OBJECT_ID('master.dbo.fn_SplitString') IS NULL BEGIN RAISERROR('Creating fn_SplitString',0,0) WITH NOWAIT; EXEC (' USE [master]; EXEC sp_executesql N'' /****************************************************************** Author: David Fowler Revision date: 01/06/2017 Version: 1 Table valued function that breaks a delimited string into a table of discrete values URL: //sqlundercover.com/2017/06/01/undercover-toolbox-fn_splitstring-its-like-string_split-but-for-luddites-or-those-who-havent-moved-to-sql-2016-yet/ © www.sqlundercover.com This script is for personal, educational, and internal corporate purposes, provided that this header is preserved. Redistribution or sale of this script,in whole or in part, is prohibited without the author''''s express written consent. 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. ******************************************************************/ CREATE FUNCTION fn_SplitString(@DelimitedString VARCHAR(MAX), @Delimiter CHAR(1) = '''','''') RETURNS @SplitStrings TABLE (StringElement VARCHAR(255)) AS BEGIN WITH Split(XMLSplit) AS (SELECT CAST('''''''' + REPLACE(@DelimitedString,@Delimiter,'''''''') + '''''''' AS XML)) INSERT INTO @SplitStrings SELECT p.value(''''.'''', ''''VARCHAR(255)'''') FROM Split CROSS APPLY XMLSplit.nodes(''''/element'''') t(p) RETURN END''; '); END IF @Compatibility = 1 OR (@Compatibility = 0 AND OBJECT_ID('master.dbo.fn_SplitString') IS NOT NULL) BEGIN IF (@DataDrive IS NOT NULL AND @LogDrive IS NOT NULL) BEGIN SET @DataDrive = REPLACE(@DataDrive,' ','') SET @LogDrive = REPLACE(@LogDrive,' ','') IF DB_NAME() = @Databasename BEGIN SET NOCOUNT ON; DECLARE @SQLStatement NVARCHAR(MAX) DECLARE @DatabaseFileSizesResult INT DECLARE @CurrentBuild VARCHAR(6) IF RIGHT(@BackupsPath,1) != '\' BEGIN SET @BackupsPath = @BackupsPath +'\' END IF OBJECT_ID('Inspector.Settings') IS NOT NULL BEGIN SELECT @CurrentBuild = (SELECT NULLIF([Value],'') FROM [Inspector].[Settings] WHERE [Description] = 'InspectorBuild') END IF @CurrentBuild IS NOT NULL BEGIN IF (@Build < @CurrentBuild) BEGIN RAISERROR('Current build: %s , is greater than the build you are trying to install (%s)',11,0,@CurrentBuild,@Build) WITH NOWAIT; RETURN; END ELSE BEGIN RAISERROR('Current build: %s , Target build: %s',0,0,@CurrentBuild,@Build) WITH NOWAIT; END END ELSE BEGIN RAISERROR('No Inspector build detected, Target build: %s',0,0,@Build) WITH NOWAIT; END --Validate installation mode, if the Inspector schema does not exist or no config exists in the Settings table --then set @InitialSetup = 1 so that a fresh install is conducted. --IF Inspector schema does not exist and @IntialSetup is not set to 1 then switch to a 1 for a fresh install IF SCHEMA_ID('Inspector') IS NULL BEGIN IF @InitialSetup = 0 BEGIN RAISERROR('Setting @InitialSetup to 1 as the Inspector schema does not exist',0,0) WITH NOWAIT; SET @InitialSetup = 1 END END ELSE --IF Schema exists BEGIN --IF Settings table does not exist and @IntialSetup is not set to 1 then switch to a 1 for a fresh install IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [schema_id] = SCHEMA_ID('Inspector') AND [name] = 'Settings') BEGIN IF @InitialSetup = 0 BEGIN RAISERROR('Setting @InitialSetup to 1 as the Inspector.Settings table does not exist',0,0) WITH NOWAIT; SET @InitialSetup = 1 END END ELSE --IF settings table exists BEGIN --IF no data exists in the Settings table and @IntialSetup is not set to 1 then switch to a 1 for a fresh install IF NOT EXISTS (SELECT [Value] FROM [Inspector].[Settings]) AND @InitialSetup = 0 BEGIN RAISERROR('Setting @InitialSetup to 1 as there is no config in [Inspector].[Settings]',0,0) WITH NOWAIT; SET @InitialSetup = 1 END END END IF (@BackupsPath IS NULL AND @InitialSetup = 1) BEGIN RAISERROR('@BackupsPath is NULL, if you plan on using the BackupSpaceCheck module be sure to insert the backup path into [Inspector].[Settings]',0,0) WITH NOWAIT; END --Check for the Inspector Schema in the reporting database and create the schema if it does not exist IF SCHEMA_ID('Inspector') IS NULL BEGIN EXEC sp_executesql N'CREATE SCHEMA [Inspector]' END --Create Inspector Upgrade table if not exists (do not drop) IF OBJECT_ID('Inspector.InspectorUpgradeHistory') IS NULL CREATE TABLE [Inspector].[InspectorUpgradeHistory]( Log_Date DATETIME, PreserveData BIT NULL, CurrentBuild DECIMAL(4,2) NULL, TargetBuild DECIMAL(4,2) NULL, SetupCommand VARCHAR(1000) NULL, RevisionDate DATE NULL ); ALTER TABLE [Inspector].[InspectorUpgradeHistory] ALTER COLUMN [CurrentBuild] DECIMAL(4,2) NULL; ALTER TABLE [Inspector].[InspectorUpgradeHistory] ALTER COLUMN [TargetBuild] DECIMAL(4,2) NULL; IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE [object_id] = OBJECT_ID(N'Inspector.InspectorUpgradeHistory') AND name = 'RevisionDate') BEGIN ALTER TABLE [Inspector].[InspectorUpgradeHistory] ADD [RevisionDate] DATE NULL; END IF OBJECT_ID('Inspector.ReportData') IS NULL CREATE TABLE [Inspector].[ReportData]( [ID] INT IDENTITY(1,1), [ReportDate] DATETIME NOT NULL, [ModuleConfig] VARCHAR(20), [ReportData] VARCHAR(MAX) NULL, [Summary] XML NULL, [Importance] VARCHAR(6) NULL, [EmailGroup] VARCHAR(50) NULL, [ReportWarningsOnly] TINYINT NULL ); IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE name = N'Summary' AND [object_id] = OBJECT_ID(N'Inspector.ReportData')) BEGIN --New column for 1.2 for Inspector.ReportData ALTER TABLE [Inspector].[ReportData] ADD [Summary] VARCHAR(60) NULL; END IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE name = N'ReportWarningsOnly' AND [object_id] = OBJECT_ID(N'Inspector.ReportData')) BEGIN ALTER TABLE [Inspector].[ReportData] ADD [ReportWarningsOnly] TINYINT NULL; END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.ReportData') AND name='IX_ReportDate') BEGIN CREATE NONCLUSTERED INDEX [IX_ReportDate] ON [Inspector].[ReportData] (ReportDate ASC); END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.ReportData') AND name='CIX_ReportData_ID') BEGIN CREATE CLUSTERED INDEX [CIX_ReportData_ID] ON [Inspector].[ReportData] (ID ASC); END --Inspector V2.00 change to XML IF NOT EXISTS (SELECT 1 FROM sys.tables INNER JOIN sys.columns ON tables.object_id = columns.object_id INNER JOIN sys.types ON columns.user_type_id = types.user_type_id WHERE tables.name = N'ReportData' AND tables.schema_id = SCHEMA_ID(N'Inspector') AND columns.name = N'Summary' AND types.name = N'xml') BEGIN ALTER TABLE [Inspector].[ReportData] ALTER COLUMN [Summary] XML NULL; END --New columns for 2.2 IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE name = N'Importance' AND [object_id] = OBJECT_ID(N'Inspector.ReportData')) BEGIN ALTER TABLE [Inspector].[ReportData] ADD [Importance] VARCHAR(6) NULL; END IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE name = N'EmailGroup' AND [object_id] = OBJECT_ID(N'Inspector.ReportData')) BEGIN ALTER TABLE [Inspector].[ReportData] ADD [EmailGroup] VARCHAR(50) NULL; END IF OBJECT_ID('Inspector.Settings') IS NULL BEGIN CREATE TABLE [Inspector].[Settings] ( [ID] INT IDENTITY(1,1), [Description] VARCHAR(100), [Value] VARCHAR(255) ); ALTER TABLE [Inspector].[Settings] ADD CONSTRAINT UC_Description UNIQUE (Description); ALTER TABLE [Inspector].[Settings] ADD CONSTRAINT [DF_Settings_Value] DEFAULT (NULL) FOR [Value]; END IF OBJECT_ID('Inspector.ModuleConfigReportExclusions') IS NULL BEGIN CREATE TABLE [Inspector].[ModuleConfigReportExclusions] ( Servername NVARCHAR(128) NOT NULL, Modulename VARCHAR(50) NOT NULL, IsActive BIT NOT NULL ); END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE [object_id] = OBJECT_ID('Inspector.ModuleConfigReportExclusions') AND [name] = N'CIX_ModuleConfigReportExclusions_Servername_Modulename') BEGIN CREATE UNIQUE CLUSTERED INDEX [CIX_ModuleConfigReportExclusions_Servername_Modulename] ON [Inspector].[ModuleConfigReportExclusions] ( Servername, Modulename ); END IF OBJECT_ID('Inspector.Modules') IS NULL BEGIN CREATE TABLE [Inspector].[Modules]( [ID] INT IDENTITY(1,1), [ModuleConfig_Desc] [varchar](20) NOT NULL, [Modulename] [varchar](50) NOT NULL, [CollectionProcedurename] [nvarchar](128) NULL, [ReportProcedurename] [nvarchar](128) NULL, [ReportOrder] TINYINT NOT NULL, [WarningLevel] TINYINT NOT NULL, [ServerSpecific] BIT NOT NULL, [Debug] BIT NOT NULL, [IsActive] BIT NOT NULL, [HeaderText] VARCHAR(100) NULL, [Frequency] SMALLINT NOT NULL, [StartTime] TIME(0) NOT NULL, [EndTime] TIME(0) NOT NULL, [LastRunDateTime] DATETIME NULL ); IF NOT EXISTS (SELECT 1 FROM sys.check_constraints WHERE name = N'CheckModuleWarningLevel' AND type = N'C' AND parent_object_id = OBJECT_ID(N'Inspector.Modules')) BEGIN EXEC sp_executesql N'ALTER TABLE [Inspector].[Modules] ADD CONSTRAINT [CheckModuleWarningLevel] CHECK ([WarningLevel] < 4);'; END IF NOT EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'Inspector.CheckModuleCollectionProc') AND parent_object_id = OBJECT_ID(N'Inspector.Modules')) BEGIN EXEC sp_executesql N'ALTER TABLE [Inspector].[Modules] WITH CHECK ADD CONSTRAINT [CheckModuleCollectionProc] CHECK (([CollectionProcedurename] IS NULL OR [CollectionProcedurename] LIKE ''%Insert''));'; END IF NOT EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'Inspector.CheckModuleReportProc') AND parent_object_id = OBJECT_ID(N'Inspector.Modules')) BEGIN EXEC sp_executesql N'ALTER TABLE [Inspector].[Modules] WITH CHECK ADD CONSTRAINT [CheckModuleReportProc] CHECK (([ReportProcedurename] IS NULL OR [ReportProcedurename] LIKE ''%Report''));'; END END --If Inspector build is less than V1.2 Column names will be different in the Module table. IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name LIKE'Enable%') BEGIN RAISERROR('Altering column names in table [Inspector].[Modules] as part of the upgrade',0,0) WITH NOWAIT; IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableADHocDatabaseCreationCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableADHocDatabaseCreationCheck', 'ADHocDatabaseCreationCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableAGCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableAGCheck', 'AGCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableBackupsCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableBackupsCheck', 'BackupsCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableBackupSizesCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableBackupSizesCheck', 'BackupSizesCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableBackupSpaceCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableBackupSpaceCheck', 'BackupSpaceCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableDatabaseFileCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableDatabaseFileCheck', 'DatabaseFileCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableDatabaseGrowthCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableDatabaseGrowthCheck', 'DatabaseGrowthCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableDatabaseOwnershipCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableDatabaseOwnershipCheck', 'DatabaseOwnershipCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableDatabaseSettings') BEGIN EXEC sp_rename 'Inspector.Modules.EnableDatabaseSettings', 'DatabaseSettings', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableDatabaseStatesCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableDatabaseStatesCheck', 'DatabaseStatesCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableDriveSpaceCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableDriveSpaceCheck', 'DriveSpaceCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableFailedAgentJobCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableFailedAgentJobCheck', 'FailedAgentJobCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableFailedLoginsCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableFailedLoginsCheck', 'FailedLoginsCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableJobOwnerCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableJobOwnerCheck', 'JobOwnerCheck', 'COLUMN'; END IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.Modules') AND name ='EnableTopFiveDatabaseSizeCheck') BEGIN EXEC sp_rename 'Inspector.Modules.EnableTopFiveDatabaseSizeCheck', 'TopFiveDatabaseSizeCheck', 'COLUMN'; END END IF OBJECT_ID('Inspector.ModuleWarningLevel') IS NULL BEGIN CREATE TABLE [Inspector].[ModuleWarningLevel] ( [ModuleConfig_Desc] VARCHAR(20) NOT NULL, [Module] VARCHAR(50) NOT NULL, [WarningLevel] TINYINT NULL ); ALTER TABLE [Inspector].[ModuleWarningLevel] ADD CONSTRAINT [UC_ModuleConfig_Desc_Module] UNIQUE CLUSTERED ( [ModuleConfig_Desc] ASC, [Module] ); END --V2.00 --If the modules table exists and its the Pre V2.00 version transfer the data to the new version of the table and rename IF EXISTS (SELECT 1 FROM sys.tables INNER JOIN sys.columns ON tables.object_id = columns.object_id WHERE tables.name = N'Modules' AND [schema_id] = SCHEMA_ID(N'Inspector') AND column_id > 2 AND columns.system_type_id = (SELECT system_type_id FROM sys.types WHERE name = N'bit') AND columns.name LIKE N'%Check') BEGIN RAISERROR('Working the magic to create the new modules table',0,0) WITH NOWAIT; --New columns for V1.3 for Inspector.Modules IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE name = N'UnusedLogshipConfig' AND [object_id] = OBJECT_ID(N'Inspector.Modules')) BEGIN ALTER TABLE [Inspector].[Modules] ADD [UnusedLogshipConfig] BIT NULL; EXEC sp_executesql N'UPDATE [Inspector].[Modules] SET [UnusedLogshipConfig] = CASE WHEN ModuleConfig_Desc = ''Default'' THEN 1 ELSE 0 END;'; END --Create new Modules table as V2 for renaming IF NOT EXISTS (SELECT 1 FROM sys.tables INNER JOIN sys.columns ON tables.object_id = columns.object_id WHERE tables.name = N'ModulesV2' AND [schema_id] = SCHEMA_ID(N'Inspector') AND columns.name IN (N'Modulename',N'CollectionProcedurename',N'ReportProcedurename',N'ReportOrder')) BEGIN CREATE TABLE [Inspector].[ModulesV2]( [ID] INT IDENTITY(1,1), [ModuleConfig_Desc] [varchar](20) NOT NULL, [Modulename] [varchar](50) NOT NULL, [CollectionProcedurename] [nvarchar](128) NULL, [ReportProcedurename] [nvarchar](128) NULL, [ReportOrder] TINYINT NOT NULL, [WarningLevel] TINYINT NOT NULL, [ServerSpecific] BIT NOT NULL, [Debug] BIT NOT NULL, [IsActive] BIT NOT NULL, [HeaderText] VARCHAR(100) NULL, [Frequency] SMALLINT NOT NULL, [StartTime] TIME(0) NOT NULL, [EndTime] TIME(0) NOT NULL, [LastRunDateTime] DATETIME NULL ); END RAISERROR('Converting old Modules table format data to the new format',0,0) WITH NOWAIT; EXEC sp_executesql N' --Transfer UseMedianCalculationForDriveSpaceCalc to the Settings table INSERT INTO [Inspector].[Settings] ([Description], [Value]) SELECT [ModulesList].[Module], CAST([Enabled] AS CHAR(1)) FROM ( SELECT ISNULL(UseMedianCalculationForDriveSpaceCalc,0) AS UseMedianCalculationForDriveSpaceCalc FROM [Inspector].[Modules] WHERE [ModuleConfig_Desc] = ''Default'' ) Modules UNPIVOT ([Enabled] FOR Module IN (UseMedianCalculationForDriveSpaceCalc) ) AS [ModulesList] WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = ''UseMedianCalculationForDriveSpaceCalc''); --Transfer Module information, only insert one row for Periodicbackupcheck as we only want the BackupsCheck Module for that moduleconfig INSERT INTO [Inspector].[ModulesV2] ([ModuleConfig_Desc], [Modulename], [CollectionProcedurename], [ReportProcedurename], [ReportOrder], [IsActive], [Debug], [ServerSpecific], [WarningLevel], [Frequency], [StartTime], [EndTime]) SELECT DISTINCT [ModulesList].[ModuleConfig_Desc], [ModulesList].[Module], [ModulesList].[Module]+''Insert'', [ModulesList].[Module]+''Report'', CASE WHEN [ModulesList].[Module] = ''ADHocDatabaseCreations'' THEN 15 WHEN [ModulesList].[Module] = ''AGCheck'' THEN 2 WHEN [ModulesList].[Module] = ''AGDatabases'' THEN 4 WHEN [ModulesList].[Module] = ''BackupsCheck'' THEN 5 WHEN [ModulesList].[Module] = ''BackupSpace'' THEN 19 WHEN [ModulesList].[Module] = ''BackupSizesByDay'' THEN 14 WHEN [ModulesList].[Module] = ''BackupSpace'' THEN 6 WHEN [ModulesList].[Module] = ''DatabaseFiles'' THEN 12 WHEN [ModulesList].[Module] = ''DatabaseGrowths'' THEN 8 WHEN [ModulesList].[Module] = ''DatabaseOwnership'' THEN 13 WHEN [ModulesList].[Module] = ''DatabaseSettings'' THEN 16 WHEN [ModulesList].[Module] = ''DatabaseStates'' THEN 7 WHEN [ModulesList].[Module] = ''DriveSpace'' THEN 1 WHEN [ModulesList].[Module] = ''FailedAgentJobs'' THEN 8 WHEN [ModulesList].[Module] = ''JobOwner'' THEN 10 WHEN [ModulesList].[Module] = ''LoginAttempts'' THEN 9 WHEN [ModulesList].[Module] = ''LongRunningTransactions'' THEN 6 WHEN [ModulesList].[Module] = ''ServerSettings'' THEN 17 WHEN [ModulesList].[Module] = ''SuspectPages'' THEN 3 WHEN [ModulesList].[Module] = ''TopFiveDatabases'' THEN 11 WHEN [ModulesList].[Module] = ''UnusedLogshipConfig'' THEN 18 END AS ReportOrder, [Enabled], 0 AS [Debug], CASE WHEN [ModulesList].[Module] = ''ADHocDatabaseCreations'' THEN 1 WHEN [ModulesList].[Module] = ''AGCheck'' THEN 1 WHEN [ModulesList].[Module] = ''AGDatabases'' THEN 1 WHEN [ModulesList].[Module] = ''BackupsCheck'' THEN 1 WHEN [ModulesList].[Module] = ''BackupSpace'' THEN 0 WHEN [ModulesList].[Module] = ''BackupSizesByDay'' THEN 1 WHEN [ModulesList].[Module] = ''BackupSpace'' THEN 0 WHEN [ModulesList].[Module] = ''DatabaseFiles'' THEN 1 WHEN [ModulesList].[Module] = ''DatabaseGrowths'' THEN 0 WHEN [ModulesList].[Module] = ''DatabaseOwnership'' THEN 1 WHEN [ModulesList].[Module] = ''DatabaseSettings'' THEN 1 WHEN [ModulesList].[Module] = ''DatabaseStates'' THEN 1 WHEN [ModulesList].[Module] = ''DriveSpace'' THEN 1 WHEN [ModulesList].[Module] = ''FailedAgentJobs'' THEN 1 WHEN [ModulesList].[Module] = ''JobOwner'' THEN 1 WHEN [ModulesList].[Module] = ''LoginAttempts'' THEN 1 WHEN [ModulesList].[Module] = ''LongRunningTransactions'' THEN 1 WHEN [ModulesList].[Module] = ''ServerSettings'' THEN 1 WHEN [ModulesList].[Module] = ''SuspectPages'' THEN 1 WHEN [ModulesList].[Module] = ''TopFiveDatabases'' THEN 1 WHEN [ModulesList].[Module] = ''UnusedLogshipConfig'' THEN 1 END AS [ServerSpecific], CASE WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''ADHocDatabaseCreations'' THEN 3 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''AGCheck'' THEN 1 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''AGDatabases'' THEN 2 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''BackupsCheck'' THEN 1 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''BackupSpace'' THEN 1 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''BackupSizesByDay'' THEN 3 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''BackupSpace'' THEN 1 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''DatabaseFiles'' THEN 2 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''DatabaseGrowths'' THEN 2 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''DatabaseOwnership'' THEN 3 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''DatabaseSettings'' THEN 3 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''DatabaseStates'' THEN 2 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''DriveSpace'' THEN 1 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''FailedAgentJobs'' THEN 2 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''JobOwner'' THEN 3 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''LoginAttempts'' THEN 3 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''LongRunningTransactions'' THEN 3 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''ServerSettings'' THEN 2 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''SuspectPages'' THEN 1 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''TopFiveDatabases'' THEN 3 WHEN [WarningLevel] IS NULL AND [ModulesList].[Module] = ''UnusedLogshipConfig'' THEN 3 WHEN [WarningLevel] > 3 THEN 3 ELSE [WarningLevel] END AS [WarningLevel], 1440, @StartTime, @EndTime --HeaderText FROM ( SELECT ModuleConfig_Desc, ISNULL(AGCheck,0) AS AGCheck, ISNULL(BackupsCheck,0) AS BackupsCheck, ISNULL(BackupSpaceCheck,0) AS BackupSpace, ISNULL(BackupSizesCheck,0) AS BackupSizesByDay, ISNULL(DatabaseGrowthCheck,0) AS DatabaseGrowths, ISNULL(DatabaseFileCheck,0) AS DatabaseFiles, ISNULL(DatabaseOwnershipCheck,0) AS DatabaseOwnership, ISNULL(DatabaseStatesCheck,0) AS DatabaseStates, ISNULL(DriveSpaceCheck,0) AS DriveSpace, ISNULL(FailedAgentJobCheck,0) AS FailedAgentJobs, ISNULL(JobOwnerCheck,0) AS JobOwner, ISNULL(FailedLoginsCheck,0) AS LoginAttempts, ISNULL(TopFiveDatabaseSizeCheck,0) AS TopFiveDatabases, ISNULL(ADHocDatabaseCreationCheck,0) AS ADHocDatabaseCreations, ISNULL(DatabaseSettings,0) AS DatabaseSettings, ISNULL(ServerSettings,0) AS ServerSettings, ISNULL(SuspectPages,0) AS SuspectPages, ISNULL(AGDatabases,0) AS AGDatabases, ISNULL(LongRunningTransactions,0) AS LongRunningTransactions, ISNULL(UnusedLogshipConfig,0) AS UnusedLogshipConfig FROM [Inspector].[Modules] ) Modules UNPIVOT ([Enabled] FOR Module IN (AGCheck ,BackupsCheck ,BackupSpace ,BackupSizesByDay ,DatabaseGrowths ,DatabaseFiles ,DatabaseOwnership ,DatabaseStates ,DriveSpace ,FailedAgentJobs ,JobOwner ,LoginAttempts ,TopFiveDatabases ,ADHocDatabaseCreations ,DatabaseSettings ,ServerSettings ,SuspectPages ,AGDatabases ,LongRunningTransactions ,UnusedLogshipConfig ) ) AS [ModulesList] LEFT JOIN [Inspector].[ModuleWarningLevel] ON CAST([ModulesList].[Module] AS VARCHAR(50)) = [ModuleWarningLevel].[Module] AND [ModulesList].[ModuleConfig_Desc] = [ModuleWarningLevel].[ModuleConfig_Desc] WHERE [ModulesList].ModuleConfig_Desc != ''PeriodicBackupCheck'' OR ([ModulesList].ModuleConfig_Desc = ''PeriodicBackupCheck'' AND [ModulesList].Module = ''BackupsCheck'') EXCEPT SELECT [ModuleConfig_Desc], [Modulename], [CollectionProcedurename], [ReportProcedurename], [ReportOrder], [IsActive], [Debug], [ServerSpecific], [WarningLevel], 1440, @StartTime, @EndTime FROM [Inspector].[ModulesV2]; UPDATE [Inspector].[ModulesV2] SET [Frequency] = 120, [StartTime] = DATEADD(HOUR,2,@StartTime), [EndTime] = @EndTime WHERE [ModuleConfig_Desc] = ''PeriodicBackupCheck'' AND Modulename = ''BackupsCheck''; INSERT INTO [Inspector].[ModulesV2] ([ModuleConfig_Desc], [Modulename], [CollectionProcedurename], [ReportProcedurename], [ReportOrder], [WarningLevel], [ServerSpecific], [Debug], [IsActive], [HeaderText], [Frequency], [StartTime], [EndTime]) VALUES(''Default'',''DatacollectionsOverdue'',''DatacollectionsOverdueInsert'',''DatacollectionsOverdueReport'',21,1,1,0,1,NULL,1440,@StartTime,@EndTime); ',N'@StartTime TIME(0),@EndTime TIME(0)',@StartTime = @StartTime,@EndTime = @EndTime; IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE parent_object_id = OBJECT_ID('Inspector.CurrentServers') AND name = N'FK_ModuleConfig_Desc') BEGIN ALTER TABLE [Inspector].[CurrentServers] DROP CONSTRAINT [FK_ModuleConfig_Desc]; END IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE parent_object_id = OBJECT_ID('Inspector.EmailConfig') AND name = N'FK_ModuleConfig_Email') BEGIN ALTER TABLE [Inspector].[EmailConfig] DROP CONSTRAINT [FK_ModuleConfig_Email]; END IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[Inspector].[FK_CatalogueModules_ModuleConfig_Desc]') AND parent_object_id = OBJECT_ID(N'[Inspector].[CatalogueModules]')) BEGIN ALTER TABLE [Inspector].[CatalogueModules] DROP CONSTRAINT [FK_CatalogueModules_ModuleConfig_Desc]; END DROP TABLE [Inspector].[Modules]; CREATE TABLE [Inspector].[Modules]( [ID] [int] IDENTITY(1,1) NOT NULL, [ModuleConfig_Desc] [varchar](20) NOT NULL, [IsActive] BIT NOT NULL, [Frequency] SMALLINT NOT NULL, [StartTime] TIME(0) NOT NULL, [EndTime] TIME(0) NOT NULL, [LastRunDateTime] DATETIME NULL, [ReportWarningsOnly] TINYINT NOT NULL, [NoClutter] BIT NOT NULL, [ShowDisabledModules] BIT NOT NULL CONSTRAINT [PK_ModuleConfig_Desc] PRIMARY KEY CLUSTERED ([ModuleConfig_Desc] ASC) ); EXEC sp_executesql N' INSERT INTO [Inspector].[Modules] ([ModuleConfig_Desc],[IsActive],[Frequency],[StartTime],[EndTime],[ReportWarningsOnly],[NoClutter],[ShowDisabledModules]) SELECT [ModuleConfig_Desc], 1, CASE WHEN [ModuleConfig_Desc] = ''PeriodicBackupCheck'' THEN 120 ELSE 1440 END, CASE WHEN [ModuleConfig_Desc] = ''PeriodicBackupCheck'' THEN ''11:00'' ELSE ''09:00'' END, ''17:30'', CASE WHEN [ModuleConfig_Desc] = ''PeriodicBackupCheck'' THEN 1 ELSE 0 END, CASE WHEN [ModuleConfig_Desc] = ''PeriodicBackupCheck'' THEN 1 ELSE 0 END, 1 FROM ( SELECT DISTINCT [ModuleConfig_Desc] FROM [Inspector].[ModulesV2] ) AS DistinctModuleConfig_Desc;'; IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[Inspector].[FK_ModuleConfig_Desc]') AND parent_object_id = OBJECT_ID(N'[Inspector].[CurrentServers]')) BEGIN ALTER TABLE [Inspector].[CurrentServers] DROP CONSTRAINT [FK_ModuleConfig_Desc]; END IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[Inspector].[FK_ModuleConfig_Desc]') AND parent_object_id = OBJECT_ID(N'[Inspector].[CurrentServers]')) BEGIN ALTER TABLE [Inspector].[CurrentServers] WITH CHECK ADD CONSTRAINT [FK_ModuleConfig_Desc] FOREIGN KEY([ModuleConfig_Desc]) REFERENCES [Inspector].[Modules] ([ModuleConfig_Desc]); ALTER TABLE [Inspector].[CurrentServers] CHECK CONSTRAINT [FK_ModuleConfig_Desc]; END IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[Inspector].[FK_ModuleConfig_Email]') AND parent_object_id = OBJECT_ID(N'[Inspector].[EmailConfig]')) BEGIN ALTER TABLE [Inspector].[EmailConfig] DROP CONSTRAINT [FK_ModuleConfig_Email]; END IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[Inspector].[FK_ModuleConfig_Email]') AND parent_object_id = OBJECT_ID(N'[Inspector].[EmailConfig]')) BEGIN ALTER TABLE [Inspector].[EmailConfig] WITH CHECK ADD CONSTRAINT [FK_ModuleConfig_Email] FOREIGN KEY([ModuleConfig_Desc]) REFERENCES [Inspector].[Modules] ([ModuleConfig_Desc]); ALTER TABLE [Inspector].[EmailConfig] CHECK CONSTRAINT [FK_ModuleConfig_Email]; END IF NOT EXISTS (SELECT 1 FROM sys.check_constraints WHERE name = N'CheckModuleWarningLevel' AND type = N'C' AND parent_object_id = OBJECT_ID(N'Inspector.ModulesV2')) BEGIN ALTER TABLE [Inspector].[ModulesV2] ADD CONSTRAINT [CheckModuleWarningLevel] CHECK ([WarningLevel] < 4); END IF NOT EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'Inspector.CheckModuleCollectionProc') AND parent_object_id = OBJECT_ID(N'Inspector.ModulesV2')) BEGIN EXEC sp_executesql N'ALTER TABLE [Inspector].[ModulesV2] WITH CHECK ADD CONSTRAINT [CheckModuleCollectionProc] CHECK (([CollectionProcedurename] IS NULL OR [CollectionProcedurename] LIKE ''%Insert''));'; END IF NOT EXISTS (SELECT * FROM sys.check_constraints WHERE object_id = OBJECT_ID(N'Inspector.CheckModuleReportProc') AND parent_object_id = OBJECT_ID(N'Inspector.ModulesV2')) BEGIN EXEC sp_executesql N'ALTER TABLE [Inspector].[ModulesV2] WITH CHECK ADD CONSTRAINT [CheckModuleReportProc] CHECK (([ReportProcedurename] IS NULL OR [ReportProcedurename] LIKE ''%Report''));'; END --Switch tables. EXEC sp_rename 'Inspector.Modules', 'ModuleConfig'; EXEC sp_rename 'Inspector.ModulesV2', 'Modules'; --Update Warning levels where the Warning level has been changed from the defaults (Keep user adjusted levels) EXEC sp_executesql N' UPDATE [Modules] SET [WarningLevel] = [ModuleWarningLevel].[WarningLevel] FROM [Inspector].[ModuleWarningLevel] INNER JOIN [Inspector].[Modules] ON [ModuleWarningLevel].[ModuleConfig_Desc] = [Modules].[ModuleConfig_Desc] AND [ModuleWarningLevel].[Module] = [Modules].[Modulename] WHERE [ModuleWarningLevel].[WarningLevel] IS NOT NULL AND [ModuleWarningLevel].[WarningLevel] != [Modules].[WarningLevel] AND [ModuleWarningLevel].[WarningLevel] < 4;' IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_ModuleConfig_Desc' AND referenced_object_id = OBJECT_ID(N'Inspector.ModuleConfig')) BEGIN ALTER TABLE [Inspector].[CurrentServers] WITH CHECK ADD CONSTRAINT [FK_ModuleConfig_Desc] FOREIGN KEY([ModuleConfig_Desc]) REFERENCES [Inspector].[ModuleConfig] ([ModuleConfig_Desc]); ALTER TABLE [Inspector].[CurrentServers] CHECK CONSTRAINT [FK_ModuleConfig_Desc]; END IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_ModuleConfig_Email' AND referenced_object_id = OBJECT_ID(N'Inspector.ModuleConfig')) BEGIN ALTER TABLE [Inspector].[EmailConfig] WITH CHECK ADD CONSTRAINT [FK_ModuleConfig_Email] FOREIGN KEY([ModuleConfig_Desc]) REFERENCES [Inspector].[ModuleConfig] ([ModuleConfig_Desc]); ALTER TABLE [Inspector].[EmailConfig] CHECK CONSTRAINT [FK_ModuleConfig_Email]; END IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_ModuleConfig_Modules' AND referenced_object_id = OBJECT_ID(N'Inspector.ModuleConfig')) BEGIN ALTER TABLE [Inspector].[Modules] WITH CHECK ADD CONSTRAINT [FK_ModuleConfig_Modules] FOREIGN KEY([ModuleConfig_Desc]) REFERENCES [Inspector].[ModuleConfig] ([ModuleConfig_Desc]); ALTER TABLE [Inspector].[Modules] CHECK CONSTRAINT [FK_ModuleConfig_Modules]; END IF OBJECT_ID('Inspector.CatalogueModules') IS NULL BEGIN CREATE TABLE [Inspector].[CatalogueModules] ( [ModuleConfig_Desc] VARCHAR(20), [Module] VARCHAR(50), [Enabled] BIT ); END IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[Inspector].[FK_CatalogueModules_ModuleConfig_Desc]') AND parent_object_id = OBJECT_ID(N'[Inspector].[CatalogueModules]')) BEGIN ALTER TABLE [Inspector].[CatalogueModules] WITH CHECK ADD CONSTRAINT [FK_CatalogueModules_ModuleConfig_Desc] FOREIGN KEY([ModuleConfig_Desc]) REFERENCES [Inspector].[ModuleConfig] ([ModuleConfig_Desc]) ALTER TABLE [Inspector].[CatalogueModules] CHECK CONSTRAINT [FK_CatalogueModules_ModuleConfig_Desc] END IF NOT EXISTS (SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[UC_CatalogueModules_ModuleConfig_Module]', 'UQ') AND parent_object_id = OBJECT_ID(N'[Inspector].[CatalogueModules]', 'U')) BEGIN EXEC sp_executesql N' ALTER TABLE [Inspector].[CatalogueModules] ADD CONSTRAINT [UC_CatalogueModules_ModuleConfig_Module] UNIQUE (ModuleConfig_Desc,Module);'; END END ELSE --If the old Modules table does not exist then check that the new Version is in place - if not then create BEGIN --Create new Modules table IF NOT EXISTS (SELECT 1 FROM sys.tables INNER JOIN sys.columns ON tables.object_id = columns.object_id WHERE tables.name = N'Modules' AND [schema_id] = SCHEMA_ID(N'Inspector') AND columns.name IN (N'Modulename',N'CollectionProcedurename',N'ReportProcedurename',N'ReportOrder')) BEGIN CREATE TABLE [Inspector].[Modules]( [ID] INT IDENTITY(1,1), [ModuleConfig_Desc] [varchar](20) NOT NULL, [Modulename] [varchar](50) NOT NULL, [CollectionProcedurename] [nvarchar](128) NULL, [ReportProcedurename] [nvarchar](128) NULL, [ReportOrder] TINYINT NOT NULL, [WarningLevel] TINYINT NOT NULL, [ServerSpecific] BIT NOT NULL, [Debug] BIT NOT NULL, [IsActive] [bit] NOT NULL, [HeaderText] VARCHAR(100) NULL, [Frequency] SMALLINT NOT NULL, [StartTime] TIME(0) NOT NULL, [EndTime] TIME(0) NOT NULL, [LastRunDateTime] DATETIME NULL ); END END IF NOT EXISTS (SELECT 1 FROM sys.tables WHERE name = N'ModuleConfig' AND [schema_id] = SCHEMA_ID(N'Inspector')) BEGIN CREATE TABLE [Inspector].[ModuleConfig]( [ID] [int] IDENTITY(1,1) NOT NULL, [ModuleConfig_Desc] [varchar](20) NOT NULL, [IsActive] BIT NOT NULL, [Frequency] SMALLINT NOT NULL, [StartTime] TIME(0) NOT NULL, [EndTime] TIME(0) NOT NULL, [LastRunDateTime] DATETIME NULL, [ReportWarningsOnly] TINYINT NOT NULL, [NoClutter] BIT NOT NULL, [ShowDisabledModules] BIT NOT NULL, [RunDay] VARCHAR(70) NULL, [EmailGroup] VARCHAR(50) NULL, [EmailProfile] NVARCHAR(128) NULL, [EmailAsAttachment] BIT NULL CONSTRAINT [PK_ModuleConfig_Desc] PRIMARY KEY CLUSTERED ([ModuleConfig_Desc] ASC) ); EXEC sp_executesql N'INSERT INTO [Inspector].[ModuleConfig] ([ModuleConfig_Desc],[IsActive],[Frequency],[StartTime],[EndTime],[ReportWarningsOnly],[NoClutter],[ShowDisabledModules],[RunDay]) VALUES (''Default'',1,1440,''09:00'',''17:30'',0,0,1,''Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday''),(''PeriodicBackupCheck'',1,120,''11:00'',''17:30'',1,1,1,''Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday'');'; END --Add new RunDay column for Specific weekday schedules IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE [object_id] = OBJECT_ID('Inspector.ModuleConfig') AND [name] = 'RunDay') BEGIN ALTER TABLE [Inspector].[ModuleConfig] ADD [RunDay] VARCHAR(70) NULL; END --Update RunDay column if it is NULL , its the same behaviour as the update but it just shows users the supported format EXEC sp_executesql N' UPDATE [Inspector].[ModuleConfig] SET [RunDay] = ''Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday'' WHERE [RunDay] IS NULL;'; IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE [object_id] = OBJECT_ID('Inspector.ModuleConfig') AND [name] = 'EmailGroup') BEGIN EXEC sp_executesql N'ALTER TABLE [Inspector].[ModuleConfig] ADD [EmailGroup] VARCHAR(50) NULL;'; END IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE [object_id] = OBJECT_ID('Inspector.ModuleConfig') AND [name] = 'EmailProfile') BEGIN EXEC sp_executesql N'ALTER TABLE [Inspector].[ModuleConfig] ADD [EmailProfile] VARCHAR(128) NULL;'; END IF NOT EXISTS (SELECT 1 FROM sys.tables INNER JOIN sys.columns ON tables.object_id = columns.object_id INNER JOIN sys.types ON columns.user_type_id = types.user_type_id WHERE tables.name = N'ModuleConfig' AND tables.schema_id = SCHEMA_ID(N'Inspector') AND columns.name = N'ReportWarningsOnly' AND types.name = N'tinyint') BEGIN ALTER TABLE [Inspector].[ModuleConfig] ALTER COLUMN [ReportWarningsOnly] TINYINT NOT NULL; END IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE [object_id] = OBJECT_ID('Inspector.ModuleConfig') AND [name] = 'EmailAsAttachment') BEGIN EXEC sp_executesql N'ALTER TABLE [Inspector].[ModuleConfig] ADD [EmailAsAttachment] BIT NULL;'; END EXEC sp_executesql N' UPDATE [Inspector].[ModuleConfig] SET [EmailAsAttachment] = 0 WHERE [EmailAsAttachment] IS NULL;'; IF OBJECT_ID('Inspector.CatalogueModules') IS NULL BEGIN CREATE TABLE [Inspector].[CatalogueModules] ( [ModuleConfig_Desc] VARCHAR(20), [Module] VARCHAR(50), [Enabled] BIT ); IF NOT EXISTS (SELECT 1 FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[UC_CatalogueModules_ModuleConfig_Module]', 'UQ') AND parent_object_id = OBJECT_ID(N'[Inspector].[CatalogueModules]', 'U')) BEGIN EXEC sp_executesql N' ALTER TABLE [Inspector].[CatalogueModules] ADD CONSTRAINT [UC_CatalogueModules_ModuleConfig_Module] UNIQUE (ModuleConfig_Desc,Module);'; END EXEC sp_executesql N' --One off population INSERT INTO [Inspector].[CatalogueModules] ([ModuleConfig_Desc],[Module],[Enabled]) SELECT [ModuleConfig].[ModuleConfig_Desc], [CatalogueModulesList].[Module], CASE WHEN [ModuleConfig_Desc] = ''Default'' THEN 1 ELSE 0 END AS [Enabled] FROM [Inspector].[ModuleConfig] CROSS JOIN (SELECT [Module] FROM (VALUES(''CatalogueMissingLogins''),(''CatalogueDroppedTables''),(''CatalogueDroppedDatabases'')) ModuleList(Module)) AS CatalogueModulesList WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[CatalogueModules] WHERE [ModuleConfig_Desc] = [ModuleConfig].[ModuleConfig_Desc] AND [Module] = [CatalogueModulesList].[Module])'; END ELSE BEGIN EXEC sp_executesql N' --Added this block for future Catalogue module additions INSERT INTO [Inspector].[CatalogueModules] ([ModuleConfig_Desc],[Module],[Enabled]) SELECT [ModuleConfig].[ModuleConfig_Desc], [CatalogueModulesList].[Module], CASE WHEN [ModuleConfig_Desc] = ''Default'' THEN 1 ELSE 0 END AS [Enabled] FROM [Inspector].[ModuleConfig] CROSS JOIN (SELECT [Module] FROM (VALUES(''CatalogueMissingLogins''),(''CatalogueDroppedTables''),(''CatalogueDroppedDatabases'')) ModuleList(Module)) AS CatalogueModulesList WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[CatalogueModules] WHERE [ModuleConfig_Desc] = [ModuleConfig].[ModuleConfig_Desc] AND [Module] = [CatalogueModulesList].[Module])'; END --Recreate the foreign keys IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_CatalogueModules_ModuleConfig_Desc' AND referenced_object_id = OBJECT_ID(N'Inspector.ModuleConfig')) BEGIN EXEC sp_executesql N' ALTER TABLE [Inspector].[CatalogueModules] WITH CHECK ADD CONSTRAINT [FK_CatalogueModules_ModuleConfig_Desc] FOREIGN KEY([ModuleConfig_Desc]) REFERENCES [Inspector].[ModuleConfig] ([ModuleConfig_Desc]); ALTER TABLE [Inspector].[CatalogueModules] CHECK CONSTRAINT [FK_CatalogueModules_ModuleConfig_Desc];'; END IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_ModuleConfig_Modules' AND referenced_object_id = OBJECT_ID(N'Inspector.ModuleConfig')) BEGIN ALTER TABLE [Inspector].[Modules] WITH CHECK ADD CONSTRAINT [FK_ModuleConfig_Modules] FOREIGN KEY([ModuleConfig_Desc]) REFERENCES [Inspector].[ModuleConfig] ([ModuleConfig_Desc]); ALTER TABLE [Inspector].[Modules] CHECK CONSTRAINT [FK_ModuleConfig_Modules]; END IF OBJECT_ID('Inspector.ADHocDatabaseCreations') IS NULL BEGIN CREATE TABLE [Inspector].[ADHocDatabaseCreations] ( [Servername] NVARCHAR(128) NOT NULL, [Log_Date] DATETIME NULL, [Databasename] NVARCHAR(128) NOT NULL, [Create_Date] DATETIME NULL ); END IF OBJECT_ID('Inspector.ADHocDatabaseSupression') IS NULL BEGIN CREATE TABLE [Inspector].[ADHocDatabaseSupression] ( [Servername] NVARCHAR(128), [Log_Date] DATETIME, [Databasename] NVARCHAR(128), [Suppress] BIT ); END IF EXISTS (SELECT * FROM sys.views WHERE [schema_id] = SCHEMA_ID(N'Inspector') AND [name]= N'MultiWarningModules') BEGIN DROP VIEW [Inspector].[MultiWarningModules]; END IF OBJECT_ID('Inspector.MultiWarningModules',N'U') IS NULL BEGIN CREATE TABLE [Inspector].[MultiWarningModules] ( [Modulename] VARCHAR(50) NULL ); END IF NOT EXISTS(SELECT 1 FROM [Inspector].[MultiWarningModules] WHERE [Modulename] IN ('DriveSpace','DatabaseGrowths','DatabaseStates','ServerSettings')) BEGIN EXEC sp_executesql N'INSERT INTO [Inspector].[MultiWarningModules] ([Modulename]) VALUES(''DriveSpace''),(''DatabaseGrowths''),(''DatabaseStates''),(''ServerSettings'');'; END IF OBJECT_ID('Inspector.AGCheck') IS NULL BEGIN CREATE TABLE [Inspector].[AGCheck]( [Servername] NVARCHAR(128) NOT NULL, [Log_Date] DATETIME NOT NULL, [AGname] NVARCHAR(128) NULL, [State] VARCHAR(50) NULL, [ReplicaServername] NVARCHAR(256) NULL, [Suspended] BIT NULL, [SuspendReason] VARCHAR(50) NULL, [FailoverReady] BIT NULL ); END IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.AGCheck') AND [name] = 'FailoverReady') BEGIN ALTER TABLE [Inspector].[AGCheck] ADD [FailoverReady] BIT; END IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.AGCheck') AND [name] = 'ReplicaRole') BEGIN ALTER TABLE [Inspector].[AGCheck] ADD [ReplicaRole] NVARCHAR(60) NULL; END IF OBJECT_ID('Inspector.AGCheckConfig') IS NULL BEGIN CREATE TABLE [Inspector].[AGCheckConfig] ( [AGname] NVARCHAR(128) NOT NULL, [AGReplicaCount] TINYINT NOT NULL, [FailoverReadyNodeCount] TINYINT NOT NULL, [FailoverReadyNodePercentCount] AS CASE WHEN [FailoverReadyNodeCount] > 10 THEN CASE WHEN CAST(ROUND([AGReplicaCount] * (CAST(FailoverReadyNodeCount AS DECIMAL(4,2))/100),0) AS TINYINT) < 1 THEN 1 ELSE CAST(ROUND([AGReplicaCount] * (CAST(FailoverReadyNodeCount AS DECIMAL(4,2))/100),0) AS TINYINT) END END, CONSTRAINT [PK_AGCheckConfig_AGname] PRIMARY KEY CLUSTERED (AGname) ); END SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' --Populate AGCheckConfig INSERT INTO [Inspector].[AGCheckConfig] ([AGname],[AGReplicaCount],[FailoverReadyNodeCount]) SELECT Groups.[name], COUNT([name]), 2 FROM sys.availability_groups Groups INNER JOIN sys.availability_replicas as Replicas ON Groups.group_id = Replicas.group_id WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[AGCheckConfig] WHERE [AGname] = Groups.[name] COLLATE DATABASE_DEFAULT) GROUP BY Groups.[name]; ' EXEC(@SQLStatement); IF OBJECT_ID('Inspector.DatabaseFiles') IS NULL CREATE TABLE [Inspector].[DatabaseFiles] ( [Servername] NVARCHAR(128) NOT NULL, [Log_Date] DATETIME NOT NULL, [Databasename] NVARCHAR(128), [FileType] VARCHAR(8), [FilePath] NVARCHAR(260) ); IF OBJECT_ID('Inspector.DatabaseStates') IS NULL CREATE TABLE [Inspector].[DatabaseStates] ( [Servername] NVARCHAR(128) NOT NULL, [Log_Date] DATETIME NOT NULL, [DatabaseState] VARCHAR(40) NULL, [Total] INT, [DatabaseNames] VARCHAR(MAX) NULL ); IF OBJECT_ID('Inspector.DriveSpace') IS NULL CREATE TABLE [Inspector].[DriveSpace] ( [Servername] NVARCHAR(128), [Log_Date] DATETIME, [Drive] NVARCHAR(128), [Capacity_GB] DECIMAL(10,2), [AvailableSpace_GB] DECIMAL(10,2), [UsedSpaceGB] DECIMAL(10,2) NULL, [PrevUsedSpace_GB] DECIMAL(10,2) NULL, [UsedSpaceVarianceGB] AS CAST([UsedSpaceGB]-[PrevUsedSpace_GB] AS DECIMAL(10,2)) ); IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.DriveSpace') AND type = 1) BEGIN IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.DriveSpace') AND name='CIX_Servername_LogDate_Drive') BEGIN CREATE CLUSTERED INDEX [CIX_Servername_LogDate_Drive] ON [Inspector].[DriveSpace] ([Servername] ASC,[Log_Date] ASC,[Drive] ASC); END END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.DriveSpace') AND name = 'IX_DriveSpace_Servername_Drive_Capacity_GB_Log_Date') BEGIN CREATE NONCLUSTERED INDEX [IX_DriveSpace_Servername_Drive_Capacity_GB_Log_Date] ON [Inspector].[DriveSpace] (Servername ASC,Drive ASC,Capacity_GB ASC,Log_Date DESC) INCLUDE (AvailableSpace_GB); END --Increase column length to accomodate shared storage names such as \\ClusterStorage IF EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.DriveSpace') AND [name] = N'Drive' AND max_length != 256) BEGIN ALTER TABLE [Inspector].[DriveSpace] ALTER COLUMN [Drive] NVARCHAR(128); END IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.DriveSpace') AND [name] = N'UsedSpaceGB') BEGIN ALTER TABLE [Inspector].[DriveSpace] ADD [UsedSpaceGB] DECIMAL(10,2) NULL; END IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.DriveSpace') AND [name] = N'PrevUsedSpace_GB') BEGIN ALTER TABLE [Inspector].[DriveSpace] ADD [PrevUsedSpace_GB] DECIMAL(10,2) NULL; END IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.DriveSpace') AND [name] = N'UsedSpaceVarianceGB') BEGIN EXEC sp_executesql N'ALTER TABLE [Inspector].[DriveSpace] ADD [UsedSpaceVarianceGB] AS CAST([UsedSpaceGB]-[PrevUsedSpace_GB] AS DECIMAL(10,2));'; END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.DriveSpace') AND name = 'IX_DriveSpace_Drive_UsedSpaceVarianceGB') BEGIN CREATE NONCLUSTERED INDEX [IX_DriveSpace_Drive_UsedSpaceVarianceGB] ON Inspector.DriveSpace ([Drive] ASC,[UsedSpaceVarianceGB] ASC); END /* One off update of UsedSpaceGB and PrevUsedSpace_GB */ EXEC sp_executesql N'UPDATE DS SET UsedSpaceGB = PrevUsedCalc.UsedSpaceGB, PrevUsedSpace_GB = PrevUsedCalc.PrevUsed FROM ( SELECT [Servername] ,[Log_Date] ,[Drive] ,[Capacity_GB]-[AvailableSpace_GB] AS UsedSpaceGB ,LAG([UsedSpaceGB],1,[UsedSpaceGB]) OVER(PARTITION BY [Servername],[Drive] ORDER BY [Log_Date] ASC) AS PrevUsed FROM [Inspector].[DriveSpace] ) AS PrevUsedCalc INNER JOIN Inspector.DriveSpace DS ON DS.Servername = PrevUsedCalc.Servername AND DS.Drive = PrevUsedCalc.Drive AND DS.Log_Date = PrevUsedCalc.Log_Date WHERE (DS.PrevUsedSpace_GB IS NULL OR DS.UsedSpaceGB IS NULL);'; IF OBJECT_ID('Inspector.FailedAgentJobs') IS NULL CREATE TABLE [Inspector].[FailedAgentJobs] ( [Servername] NVARCHAR(128) NOT NULL, [Log_Date] DATETIME NOT NULL, [Jobname] VARCHAR(128) NULL, [LastStepFailed] TINYINT NULL, [LastFailedDate] DATETIME NULL, [LastError] VARCHAR(260) NULL ); IF OBJECT_ID('Inspector.LoginAttempts') IS NULL CREATE TABLE [Inspector].[LoginAttempts] ( [Servername] NVARCHAR(128) NOT NULL, [Log_Date] DATETIME NOT NULL, [Username] VARCHAR(50) NULL, [Attempts] INT NULL, [LastErrorDate] DATETIME NULL, [LastError] VARCHAR(260) NULL ); IF OBJECT_ID('Inspector.JobOwner') IS NULL CREATE TABLE [Inspector].[JobOwner] ( [Servername] NVARCHAR(128) NOT NULL, [Log_Date] DATETIME NOT NULL, [Job_ID] UNIQUEIDENTIFIER NULL, [Jobname] VARCHAR(100) NOT NULL ); IF OBJECT_ID('Inspector.TopFiveDatabases') IS NULL CREATE TABLE [Inspector].[TopFiveDatabases] ( [Servername] NVARCHAR(128) NOT NULL, [Log_Date] DATETIME NOT NULL, [Databasename] NVARCHAR(128) NULL, [TotalSize_MB] BIGINT ); IF OBJECT_ID('Inspector.BackupsCheck') IS NULL --New columns for 1.2 [primary_replica],[backup_preference] CREATE TABLE [Inspector].[BackupsCheck]( [Servername] NVARCHAR (128) NOT NULL, [Log_Date] [datetime] NOT NULL, [Databasename] [nvarchar](128) NULL, [AGname] Nvarchar (128) NULL, [FULL] [datetime] NULL, [DIFF] [datetime] NULL, [LOG] [datetime] NULL, [IsFullRecovery] [bit] NULL, [IsSystemDB] [bit] NULL, [primary_replica] [nvarchar](128) NULL, [backup_preference] [nvarchar](60) NULL ); --New columns for 1.2 [primary_replica],[backup_preference] IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.BackupsCheck') AND name ='primary_replica') BEGIN ALTER TABLE [Inspector].[BackupsCheck] ADD [primary_replica] [nvarchar](128) NULL; END IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.BackupsCheck') AND name ='backup_preference') BEGIN ALTER TABLE [Inspector].[BackupsCheck] ADD [backup_preference] [nvarchar](60) NULL; END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.BackupsCheck') AND name = 'CIX_BackupsCheck_Servername_Databasename') BEGIN CREATE CLUSTERED INDEX [CIX_BackupsCheck_Servername_Databasename] ON [Inspector].[BackupsCheck] ( [Servername], [Databasename] ); END IF OBJECT_ID('Inspector.BackupsCheckThresholds') IS NULL BEGIN CREATE TABLE [Inspector].[BackupsCheckThresholds] ( [Servername] NVARCHAR(128) NOT NULL, [Databasename] NVARCHAR(128) NOT NULL, [FullThreshold] INT NOT NULL, [DiffThreshold] INT NULL, [LogThreshold] INT NULL, [IsActive] BIT NOT NULL ); EXEC sp_executesql N'CREATE UNIQUE CLUSTERED INDEX [CIX_BackupsCheckThresholds_IsActive_Servername_Databasename] ON [Inspector].[BackupsCheckThresholds] ( [Servername], [Databasename] );'; END IF OBJECT_ID('Inspector.DatabaseFileSizes') IS NULL BEGIN --New Column [LastUpdated] for 1.0.1 CREATE TABLE [Inspector].[DatabaseFileSizes]( [Servername] NVARCHAR(128) NOT NULL, [Database_id] INT NOT NULL, [Database_name] NVARCHAR(128) NULL, [OriginalDateLogged] DATETIME NOT NULL, [OriginalSize_MB] BIGINT NULL, [Type_desc] NVARCHAR(60) NULL, [File_id] INT NOT NULL, [Filename] NVARCHAR(260) NULL, [PostGrowthSize_MB] BIGINT NULL, [GrowthRate] INT NULL, [Is_percent_growth] BIT NOT NULL, [NextGrowth] BIGINT NULL, [LastUpdated] DATETIME NULL ); END --Change data type for File_id #205 IF EXISTS(SELECT * FROM sys.columns WHERE [object_id] = OBJECT_ID(N'Inspector.DatabaseFileSizes') AND [name] = N'File_id' AND [user_type_id] = (SELECT user_type_id FROM sys.types WHERE [name] = N'tinyint')) BEGIN ALTER TABLE [Inspector].[DatabaseFileSizes] ALTER COLUMN [File_id] INT NOT NULL; END --New Column [LastUpdated] for 1.0.1 IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.DatabaseFileSizes') AND name ='LastUpdated') BEGIN ALTER TABLE [Inspector].[DatabaseFileSizes] ADD [LastUpdated] DATETIME NULL; END ; SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' --Make a one off update to the Database files to ensure they now show the full path and not just the filename UPDATE [DatabaseFileSizes] SET [Filename] = [master_files].[physical_name],[LastUpdated] = GETDATE() FROM sys.master_files INNER JOIN [Inspector].[DatabaseFileSizes] ON [DatabaseFileSizes].[Database_id] = [master_files].[database_id] AND [DatabaseFileSizes].[File_id] = [master_files].[file_id] WHERE [DatabaseFileSizes].[Servername] = @@SERVERNAME;'; EXEC(@SQLStatement); IF OBJECT_ID('Inspector.DatabaseFileSizeHistory') IS NULL BEGIN CREATE TABLE [Inspector].DatabaseFileSizeHistory ( [GrowthID] BIGINT IDENTITY(1,1), [Servername] NVARCHAR(128) NOT NULL, [Database_id] INT NOT NULL, [Database_name] NVARCHAR(128) NOT NULL, [Log_Date] DATETIME NOT NULL, [Type_Desc] NVARCHAR(60) NOT NULL, [File_id] INT NOT NULL, [FileName] NVARCHAR(260) NOT NULL, [PreGrowthSize_MB] BIGINT NOT NULL, [GrowthRate_MB] INT NOT NULL, [GrowthIncrements] INT NOT NULL, [PostGrowthSize_MB] BIGINT NOT NULL, [Drive] NVARCHAR(128) ); END --Change data type for File_id #205 IF EXISTS(SELECT * FROM sys.columns WHERE [object_id] = OBJECT_ID(N'Inspector.DatabaseFileSizeHistory') AND [name] = N'File_id' AND [user_type_id] = (SELECT user_type_id FROM sys.types WHERE [name] = N'tinyint')) BEGIN ALTER TABLE [Inspector].[DatabaseFileSizeHistory] ALTER COLUMN [File_id] INT NOT NULL; END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.DatabaseFileSizeHistory') AND name='IX_Servername_Includes_Log_Date') BEGIN CREATE NONCLUSTERED INDEX [IX_Servername_Includes_Log_Date] ON [Inspector].[DatabaseFileSizeHistory] ([Servername] ASC) INCLUDE ([Log_Date]); END IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE name = N'Drive' AND [object_id] = OBJECT_ID(N'Inspector.DatabaseFileSizeHistory')) BEGIN --New column for 1.4 ALTER TABLE [Inspector].[DatabaseFileSizeHistory] ADD [Drive] NVARCHAR(128); END --Increase column length to accomodate shared storage names such as \\ClusterStorage IF EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.DatabaseFileSizeHistory') AND [name] = 'Drive' AND max_length != 256) BEGIN ALTER TABLE [Inspector].[DatabaseFileSizeHistory] ALTER COLUMN [Drive] NVARCHAR(128); END IF OBJECT_ID('Inspector.EmailRecipients') IS NULL CREATE TABLE [Inspector].[EmailRecipients] ( ID INT IDENTITY(1,1), Description VARCHAR(50) NOT NULL, Recipients VARCHAR(1000) DEFAULT NULL CONSTRAINT UC_EmailDescription UNIQUE (Description) ); --NEw FK for 2.2 IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = 'FK_ModuleConfig_EmailGroup') BEGIN EXEC sp_executesql N'ALTER TABLE [Inspector].[ModuleConfig] WITH CHECK ADD CONSTRAINT [FK_ModuleConfig_EmailGroup] FOREIGN KEY (EmailGroup) REFERENCES [Inspector].[EmailRecipients]([Description]);'; END IF OBJECT_ID('Inspector.CurrentServers') IS NULL BEGIN CREATE TABLE [Inspector].[CurrentServers] ( [Servername] [Nvarchar](128) NULL, [IsActive] BIT, [ModuleConfig_Desc] VARCHAR(20), TableHeaderColour VARCHAR(7) CONSTRAINT UC_Servername UNIQUE (Servername) ); ALTER TABLE Inspector.CurrentServers ADD CONSTRAINT FK_ModuleConfig_Desc FOREIGN KEY (ModuleConfig_Desc) REFERENCES Inspector.ModuleConfig(ModuleConfig_Desc); END IF OBJECT_ID('Inspector.EmailConfig') IS NULL CREATE TABLE [Inspector].[EmailConfig] ( ModuleConfig_Desc VARCHAR(20), EmailSubject VARCHAR(100) DEFAULT NULL, CONSTRAINT FK_ModuleConfig_Email FOREIGN KEY (ModuleConfig_Desc) REFERENCES Inspector.ModuleConfig(ModuleConfig_Desc) ); IF OBJECT_ID('Inspector.ModuleWarnings') IS NULL BEGIN CREATE TABLE [Inspector].[ModuleWarnings] ( [WarningLevel] TINYINT NULL, [WarningDesc] VARCHAR(30), [HighlightHtmlColor] VARCHAR(7) NULL, [GradientLeftHtmlColor] VARCHAR(7) NULL, [GradientRightHtmlColor] VARCHAR(7) NULL ); --Insert Module warning level descriptions (new feature for V1.3) EXEC sp_executesql N' INSERT INTO [Inspector].[ModuleWarnings] ([WarningLevel],[WarningDesc],[HighlightHtmlColor],[GradientLeftHtmlColor],[GradientRightHtmlColor]) VALUES(NULL,''InspectorDefault'',NULL,NULL,NULL),(1,''Warning'',''#fc5858'',''#000000'',''#fc5858''),(2,''Advisory'',''#FAFCA4'',''#000000'',''#FAFCA4''),(3,''Informational'',''#FEFFFF'',''#000000'',''#FEFFFF'');'; ALTER TABLE [Inspector].[ModuleWarnings] ADD CONSTRAINT [UC_WarningLevel] UNIQUE CLUSTERED ([WarningLevel] ASC); END ELSE BEGIN IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.ModuleWarnings') AND name='UC_WarningLevel') BEGIN BEGIN TRY ALTER TABLE [Inspector].[ModuleWarnings] ADD CONSTRAINT [UC_WarningLevel] UNIQUE CLUSTERED ([WarningLevel] ASC); END TRY BEGIN CATCH RAISERROR('Unable to create Unique Clustered index [UC_WarningLevel] on [Inspector].[ModuleWarnings]',0,0) END CATCH END IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.ModuleWarnings') AND name='HighlightHtmlColor') BEGIN ALTER TABLE [Inspector].[ModuleWarnings] ADD [HighlightHtmlColor] VARCHAR(7) NULL; EXEC sp_executesql N' UPDATE [Inspector].[ModuleWarnings] SET [HighlightHtmlColor] = ''#fc5858'' WHERE [WarningLevel] = 1; UPDATE [Inspector].[ModuleWarnings] SET [HighlightHtmlColor] = ''#FAFCA4'' WHERE [WarningLevel] = 2; UPDATE [Inspector].[ModuleWarnings] SET [HighlightHtmlColor] = ''#FEFFFF'' WHERE [WarningLevel] = 3;'; END IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.ModuleWarnings') AND name='GradientLeftHtmlColor') BEGIN ALTER TABLE [Inspector].[ModuleWarnings] ADD [GradientLeftHtmlColor] VARCHAR(7) NULL; EXEC sp_executesql N' UPDATE [Inspector].[ModuleWarnings] SET [GradientLeftHtmlColor] = ''#000000'' WHERE [WarningLevel] = 1; UPDATE [Inspector].[ModuleWarnings] SET [GradientLeftHtmlColor] = ''#000000'' WHERE [WarningLevel] = 2; UPDATE [Inspector].[ModuleWarnings] SET [GradientLeftHtmlColor] = ''#000000'' WHERE [WarningLevel] = 3;'; END IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.ModuleWarnings') AND name='GradientRightHtmlColor') BEGIN ALTER TABLE [Inspector].[ModuleWarnings] ADD [GradientRightHtmlColor] VARCHAR(7) NULL; EXEC sp_executesql N' UPDATE [Inspector].[ModuleWarnings] SET [GradientRightHtmlColor] = ''#fc5858'' WHERE [WarningLevel] = 1; UPDATE [Inspector].[ModuleWarnings] SET [GradientRightHtmlColor] = ''#FAFCA4'' WHERE [WarningLevel] = 2; UPDATE [Inspector].[ModuleWarnings] SET [GradientRightHtmlColor] = ''#FEFFFF'' WHERE [WarningLevel] = 3;'; END END IF OBJECT_ID('Inspector.DatabaseOwnership') IS NULL CREATE TABLE [Inspector].[DatabaseOwnership] ( [Servername] [nvarchar](128) NOT NULL, [Log_Date] DATETIME NULL, [AGname] [nvarchar](128) NULL, [Database_name] [nvarchar](128) NOT NULL, [Owner] [nvarchar](100) NULL ); IF OBJECT_ID('Inspector.BackupSizesByDay') IS NULL CREATE TABLE [Inspector].[BackupSizesByDay] ( [Servername] [nvarchar](128) NOT NULL, [Log_Date] DATETIME NULL, [DayOfWeek] [VARCHAR](10) NULL, [CastedDate] [DATE] NULL, [TotalSizeInBytes] [BIGINT] NULL ); IF OBJECT_ID('Inspector.DatabaseSettings') IS NULL CREATE TABLE [Inspector].[DatabaseSettings]( [Servername] [nvarchar](128) NULL, [Log_Date] [datetime] NULL, [Setting] [varchar](50) NULL, [Description] [varchar](100) NULL, [Total] [int] NULL ); IF OBJECT_ID('Inspector.ServerSettings') IS NULL CREATE TABLE [Inspector].[ServerSettings]( [Servername] NVARCHAR(128) NULL, [Log_Date] DATETIME NULL, [configuration_id] INT NULL, [Setting] NVARCHAR(128) NULL, [value_in_use] INT NULL, [LastUpdated] DATETIME NOT NULL ); IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.ServerSettings') AND [name] = N'CIX_Servername_Setting') BEGIN EXEC sp_executesql N'CREATE CLUSTERED INDEX [CIX_Servername_Setting] ON [Inspector].[ServerSettings] ([Servername],[Setting]);'; END IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.ServerSettings') AND [name] = N'LastUpdated') BEGIN ALTER TABLE [Inspector].[ServerSettings] ADD [LastUpdated] DATETIME NULL; END EXEC sp_executesql N' UPDATE [Inspector].[ServerSettings] SET [LastUpdated] = [Log_Date] WHERE [LastUpdated] IS NULL;'; IF EXISTS(SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.ServerSettings') AND [name] = N'LastUpdated' AND is_nullable = 1) BEGIN ALTER TABLE [Inspector].[ServerSettings] ALTER COLUMN [LastUpdated] DATETIME NOT NULL; END /* On off population with IsActtive = 0*/ EXEC sp_executesql N' INSERT INTO [Inspector].[ServerSettings] ([Servername], [Log_Date], [configuration_id], [Setting], [value_in_use], [LastUpdated]) SELECT @@SERVERNAME, GETDATE(), [configuration_id], [name], CAST([value_in_use] AS INT), GETDATE() FROM sys.configurations conf WHERE NOT EXISTS(SELECT 1 FROM [Inspector].[ServerSettings] ssc WHERE [conf].[name] = [ssc].[Setting] AND [ssc].[Servername] = @@SERVERNAME);'; IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[ServerSettingsConfig]') AND type in (N'U')) BEGIN CREATE TABLE [Inspector].[ServerSettingsConfig] ( [Servername] NVARCHAR(128), [Setting] NVARCHAR(128), [value_in_use] INT, [IsActive] BIT ); END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.ServerSettingsConfig') AND [name] = N'CIX_Servername_IsActive_Setting') BEGIN EXEC sp_executesql N'CREATE CLUSTERED INDEX [CIX_Servername_IsActive_Setting] ON [Inspector].[ServerSettingsConfig] ([Servername],[IsActive],[Setting]);'; END /* On off population with IsActtive = 0*/ EXEC sp_executesql N' INSERT INTO [Inspector].[ServerSettingsConfig] ([Servername],[Setting],[value_in_use],[IsActive]) SELECT @@SERVERNAME, [name], CAST([value_in_use] AS INT), 0 FROM sys.configurations conf WHERE NOT EXISTS(SELECT 1 FROM [Inspector].[ServerSettingsConfig] ssc WHERE [conf].[name] = [ssc].[Setting] AND [ssc].[Servername] = @@SERVERNAME);'; IF NOT EXISTS(SELECT 1 FROM [Inspector].[MultiWarningModules] WHERE [Modulename] = 'ServerSettings') BEGIN INSERT INTO [Inspector].[MultiWarningModules] ([Modulename]) VALUES('ServerSettings'); END IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[ServerSettingsAudit]') AND type in (N'U')) BEGIN CREATE TABLE [Inspector].[ServerSettingsAudit]( [ID] INT IDENTITY(1,1), [Servername] NVARCHAR(128) NULL, [Log_Date] DATETIME NULL, [configuration_id] INT NULL, [Setting] NVARCHAR(128) NULL, [old_value_in_use] INT NULL, [value_in_use] INT NULL, [AuditDate] DATETIME NULL, [PrevLastUpdated] DATETIME NOT NULL, [config_value_in_use] INT ); END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.ServerSettingsAudit') AND [name] = N'CIX_Servername_Setting_AuditDate') BEGIN EXEC sp_executesql N'CREATE CLUSTERED INDEX [CIX_Servername_Setting_AuditDate] ON [Inspector].[ServerSettingsAudit] ([Servername],[Setting],[AuditDate]);'; END IF OBJECT_ID('Inspector.ServerInfo') IS NULL BEGIN CREATE TABLE [Inspector].[ServerInfo] ( Servername NVARCHAR(128), Log_Date DATETIME, cpu_count INT, hyperthread_count INT, physical_memory_gb INT, scheduler_count INT, affinity_type_desc NVARCHAR(10), machine_type NVARCHAR(10) ); END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.ServerInfo') AND [name] = N'CIX_ServerInfo_Servername_Log_Date') BEGIN EXEC sp_executesql N'CREATE UNIQUE CLUSTERED INDEX [CIX_ServerInfo_Servername_Log_Date] ON [Inspector].[ServerInfo] ([Servername],[Log_Date]);'; END IF NOT EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[Inspector].[ServerSettingsChangeAudit]')) BEGIN EXEC dbo.sp_executesql @statement = N' CREATE TRIGGER [Inspector].[ServerSettingsChangeAudit] ON [Inspector].[ServerSettings] AFTER UPDATE AS BEGIN SET NOCOUNT ON; INSERT INTO [Inspector].[ServerSettingsAudit] ([Servername],[Log_Date],[configuration_id],[Setting],[old_value_in_use],[value_in_use],[AuditDate],[PrevLastUpdated],[config_value_in_use]) SELECT i.[Servername], i.[Log_Date], i.[configuration_id], i.[Setting], d.[value_in_use], i.[value_in_use], i.[LastUpdated], d.[LastUpdated], (SELECT value_in_use FROM [Inspector].[ServerSettingsConfig] ssc WHERE i.[Servername] = ssc.[Servername] AND i.[Setting] = ssc.[Setting] AND ssc.[IsActive] = 1) FROM inserted i INNER JOIN deleted d ON i.[Servername] = d.[Servername] AND i.[Setting] = d.[Setting] AND i.value_in_use != d.value_in_use; END '; END; IF OBJECT_ID('Inspector.InstanceStart') IS NULL CREATE TABLE [Inspector].[InstanceStart]( Servername NVARCHAR(128), Log_Date DATETIME, InstanceStart DATETIME ); IF OBJECT_ID('Inspector.SuspectPages') IS NULL CREATE TABLE [Inspector].[SuspectPages]( [Servername] NVARCHAR(128), [Log_Date] DATETIME, [Databasename] NVARCHAR(128), [file_id] INT, [page_id] BIGINT, [event_type] INT, [error_count] INT, [last_update_date] DATETIME ); IF OBJECT_ID('Inspector.AGDatabases') IS NULL CREATE TABLE [Inspector].[AGDatabases]( [ID] INT IDENTITY(1,1), [Servername] NVARCHAR(128) NULL, [Log_Date] DATETIME NULL, [LastUpdated] DATETIME NULL, [Databasename] NVARCHAR(128) NULL, [Is_AG] BIT NULL, [Is_AGJoined] BIT NULL ); IF OBJECT_ID('Inspector.InstanceVersion') IS NULL CREATE TABLE [Inspector].[InstanceVersion]( Servername NVARCHAR(128), PhysicalServername NVARCHAR(128), Log_Date DATETIME, VersionInfo NVARCHAR(128) ); IF OBJECT_ID('Inspector.LongRunningTransactions') IS NULL CREATE TABLE [Inspector].[LongRunningTransactions]( [Servername] NVARCHAR(128) NULL, [Log_Date] DATETIME NULL, [session_id] INT NULL, [transaction_begin_time] DATETIME NULL, [Duration_DDHHMMSS] VARCHAR(20) NULL, [TransactionState] VARCHAR(20) NULL, [SessionState] NVARCHAR(20) NULL, [login_name] NVARCHAR(128) NULL, [host_name] NVARCHAR(128) NULL, [program_name] NVARCHAR(128) NULL, [Databasename] NVARCHAR(128) NULL, [ThresholdType] VARCHAR(128) NULL, [Querytext] NVARCHAR(MAX) NULL ); IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.LongRunningTransactions') AND name = 'Querytext') BEGIN ALTER TABLE [Inspector].[LongRunningTransactions] ADD [Querytext] NVARCHAR(MAX) NULL; END /* If the column exists already be sure it is varchar(128) as it started life as varchar(60) during development */ IF EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.LongRunningTransactions') AND [name] = N'ThresholdType' AND max_length != 128) BEGIN ALTER TABLE [Inspector].[LongRunningTransactions] ALTER COLUMN [ThresholdType] VARCHAR(128); END IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.LongRunningTransactions') AND name = 'ThresholdType') BEGIN ALTER TABLE [Inspector].[LongRunningTransactions] ADD [ThresholdType] VARCHAR(128) NULL; END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID('Inspector.LongRunningTransactions') AND [name] = N'CIX_LongRunningTransactions_Servername') BEGIN EXEC sp_executesql N'CREATE CLUSTERED INDEX [CIX_LongRunningTransactions_Servername] ON [Inspector].[LongRunningTransactions] ([Servername]);'; END IF OBJECT_ID('Inspector.LongRunningTransactionsHistory') IS NULL BEGIN CREATE TABLE [Inspector].[LongRunningTransactionsHistory]( [ID] INT IDENTITY(1,1), [Servername] NVARCHAR(128) NULL, [Log_Date] DATETIME NULL, [session_id] INT NULL, [transaction_begin_time] DATETIME NULL, [Duration_DDHHMMSS] VARCHAR(20) NULL, [TransactionState] VARCHAR(20) NULL, [SessionState] NVARCHAR(20) NULL, [login_name] NVARCHAR(128) NULL, [host_name] NVARCHAR(128) NULL, [program_name] NVARCHAR(128) NULL, [Databasename] NVARCHAR(128) NULL, [ThresholdType] VARCHAR(128) NULL, [Querytext] NVARCHAR(MAX) NULL ); EXEC sp_executesql N'CREATE UNIQUE CLUSTERED INDEX [CIX_ID] ON [Inspector].[LongRunningTransactionsHistory] ([ID] ASC);'; EXEC sp_executesql N'CREATE NONCLUSTERED INDEX [CIX_Log_Date_Servername] ON [Inspector].[LongRunningTransactionsHistory] ([Log_Date] ASC,[Servername] ASC);'; END /* If the column exists already be sure it is varchar(128) as it started life as varchar(60) during development */ IF EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.LongRunningTransactionsHistory') AND [name] = N'ThresholdType' AND max_length != 128) BEGIN ALTER TABLE [Inspector].[LongRunningTransactionsHistory] ALTER COLUMN [ThresholdType] VARCHAR(128); END IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.LongRunningTransactionsHistory') AND name = 'ThresholdType') BEGIN ALTER TABLE [Inspector].[LongRunningTransactionsHistory] ADD [ThresholdType] VARCHAR(128) NULL; END IF OBJECT_ID('Inspector.LongRunningTransactionsThresholds') IS NULL BEGIN CREATE TABLE [Inspector].[LongRunningTransactionsThresholds] ( [ID] INT IDENTITY(1,1), [Servername] NVARCHAR(128) NOT NULL, [login_name] NVARCHAR(128) NULL, [host_name] NVARCHAR(128) NULL, [program_name] NVARCHAR(128) NULL, [DurationInSeconds] INT NOT NULL, [IsActive] BIT NOT NULL, CONSTRAINT CheckTransactionsThresholds CHECK (([login_name] IS NOT NULL) OR ([host_name] IS NOT NULL) OR ([program_name] IS NOT NULL)) ); EXEC sp_executesql N'CREATE CLUSTERED INDEX [CIX_LongRunningTransactionsThresholds_Servername_IsActive] ON [Inspector].[LongRunningTransactionsThresholds] ( [Servername] ASC, [IsActive] DESC );'; END IF NOT EXISTS(SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'LongRunningTransactionsHistoryRetentionDays') BEGIN INSERT INTO [Inspector].[Settings] ([Description], [Value]) VALUES('LongRunningTransactionsHistoryRetentionDays','7'); END IF OBJECT_ID('Inspector.InstanceVersionHistory') IS NULL CREATE TABLE [Inspector].[InstanceVersionHistory]( [Servername] NVARCHAR(128) NOT NULL, [Log_Date] DATETIME NOT NULL, [CollectionDatetime] DATETIME NOT NULL, [VersionNo] NVARCHAR(128) NULL, [Edition] NVARCHAR(128) NULL ); --Extend the length of the VersionNo column to NVARCHAR(128) to accomodate additionally logged information #93 IF (SELECT max_length FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.InstanceVersionHistory') AND name = 'VersionNo') < 256 BEGIN ALTER TABLE [Inspector].[InstanceVersionHistory] ALTER COLUMN [VersionNo] NVARCHAR(128); END IF OBJECT_ID('Inspector.UnusedLogshipConfig') IS NULL CREATE TABLE [Inspector].[UnusedLogshipConfig] ( Servername NVARCHAR(128), Log_Date DATETIME, Databasename NVARCHAR(128), Databasestate NVARCHAR(128) ); IF OBJECT_ID('Inspector.ExecutionLog') IS NULL BEGIN CREATE TABLE [Inspector].[ExecutionLog]( [ID] INT IDENTITY(1,1), [ExecutionDate] DATETIME NOT NULL, [Servername] NVARCHAR(128) NOT NULL, [ModuleConfig_Desc] VARCHAR(20) NOT NULL, [Procname] NVARCHAR(128) NOT NULL, [Frequency] SMALLINT NULL, [Duration] MONEY, [PSCollection] BIT NOT NULL, [ErrorMessage] NVARCHAR(128) NULL ); CREATE CLUSTERED INDEX [CIX_ExecutionLog_ID] ON [Inspector].[ExecutionLog] ([ID] ASC); END IF NOT EXISTS(SELECT 1 FROM sys.tables INNER JOIN sys.columns ON tables.object_id = columns.object_id WHERE tables.schema_id = SCHEMA_ID(N'Inspector') AND tables.name = N'ExecutionLog' AND columns.name = N'Frequency') BEGIN ALTER TABLE [Inspector].[ExecutionLog] ADD [Frequency] SMALLINT NULL; END IF NOT EXISTS(SELECT 1 FROM sys.tables INNER JOIN sys.columns ON tables.object_id = columns.object_id WHERE tables.schema_id = SCHEMA_ID(N'Inspector') AND tables.name = N'ExecutionLog' AND columns.name = N'ErrorMessage') BEGIN ALTER TABLE [Inspector].[ExecutionLog] ADD [ErrorMessage] NVARCHAR(128) NULL; END IF OBJECT_ID('Inspector.CatalogueSIDExclusions') IS NULL BEGIN CREATE TABLE [Inspector].[CatalogueSIDExclusions] ( AGName NVARCHAR(128), LoginName NVARCHAR(128) ); END IF OBJECT_ID('Inspector.BackupsCheckExcludes') IS NULL BEGIN CREATE TABLE [Inspector].[BackupsCheckExcludes] ( Servername NVARCHAR(128) NOT NULL, Databasename NVARCHAR(128) NOT NULL, SuppressUntil DATETIME NULL ); CREATE CLUSTERED INDEX [CIX_Servername_Databasename] ON [Inspector].[BackupsCheckExcludes] ([Servername] ASC,[Databasename] ASC); END IF OBJECT_ID('Inspector.AGPrimaryHistory') IS NULL BEGIN CREATE TABLE [Inspector].[AGPrimaryHistory]( [Log_Date] DATETIME NULL, [CollectionDateTime] DATETIME NULL, [Servername] NVARCHAR(128) NULL, [AGname] NVARCHAR(128) NULL ); END IF OBJECT_ID('Inspector.DriveSpaceCalc') IS NULL BEGIN CREATE TABLE [Inspector].[DriveSpaceCalc] ( [Servername] NVARCHAR(128) NOT NULL, [Drive] NVARCHAR(128) NOT NULL, [MedianCalc] BIT NOT NULL ); END IF OBJECT_ID('Inspector.BackupSpace') IS NULL BEGIN CREATE TABLE [Inspector].[BackupSpace]( [Servername] [nvarchar](128) NOT NULL, [Log_Date] [datetime] NULL, [DayOfWeek] [varchar](10) NULL, [CastedDate] [date] NULL, [BackupPath] [varchar](256) NULL, [TotalSizeInBytes] [bigint] NULL ); END IF OBJECT_ID('Inspector.DefaultHeaderText') IS NULL BEGIN CREATE TABLE [Inspector].[DefaultHeaderText]( [Modulename] [varchar](128) NOT NULL, [HeaderText] [varchar](100) NOT NULL ); --Populate the default text EXEC sp_executesql N' IF NOT EXISTS (SELECT 1 FROM [Inspector].[DefaultHeaderText]) BEGIN INSERT INTO [Inspector].[DefaultHeaderText] ([Modulename], [HeaderText]) VALUES(''ADHocDatabaseCreations'',''Potential ADhoc database creations''), (''AGCheck'',''AG Warnings''), (''AGDatabases'',''Databases not joined to an Availability group''), (''BackupsCheck '',''Database Backup issues''), (''BackupSpace'',''Backup space issues''), (''CatalogueDroppedDatabases'',''Dropped databases''), (''CatalogueDroppedTables'',''Dropped tables''), (''CatalogueMissingLogins'',''Missing Logins''), (''DatabaseFiles'',''Database files on incorrect drives''), (''DatabaseGrowths'',''Database Growths in the last 24 hours exceeding thresholds''), (''DatabaseOwnership'',''Database ownerships not set to your preferred owner''), (''DatabaseSettings'',''Database Auto Close or Auto Shrink settings enabled or Auto Update Stats Disabled''), (''DatabaseStates'',''Database state warnings''), (''DriveSpace'',''Drive space thresholds breached''), (''FailedAgentJobs'',''Failed agent jobs''), (''LoginAttempts'',''Failed logins''), (''JobOwner'',''Agent jobs not set to your preferred owner''), (''LongRunningTransactions'',''Long running transactions exceeding your threshold''), (''ServerSettings'',''Server settings changed or differ from your config values''), (''SuspectPages'',''Suspect database pages found''), (''UnusedLogshipConfig'',''Unused log shipping config found''), (''DatacollectionsOverdue'',''Data collection duration exceeded module schedules''); END'; IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'CIX_DefaultHeaderText_Modulename' AND [object_id] = OBJECT_ID(N'Inspector.DefaultHeaderText')) BEGIN EXEC sp_executesql N'CREATE UNIQUE CLUSTERED INDEX [CIX_DefaultHeaderText_Modulename] ON [Inspector].[DefaultHeaderText] ([Modulename] ASC);'; END END EXEC sp_executesql N' UPDATE [Inspector].[DefaultHeaderText] SET [HeaderText] = ''Server settings changed or differ from your config values'' WHERE [Modulename] = ''ServerSettings'' AND [HeaderText] = ''Cost Threshold for parallelism, MAXDOP or Max Server memory set to default values'';'; EXEC sp_executesql N' IF NOT EXISTS (SELECT 1 FROM [Inspector].[DefaultHeaderText] WHERE [Modulename] = ''DriveSpace'') BEGIN INSERT INTO [Inspector].[DefaultHeaderText]([Modulename], [HeaderText]) VALUES(''DriveSpace'',''Drive space thresholds breached''); END'; IF OBJECT_ID('Inspector.DatacollectionsOverdue') IS NULL BEGIN CREATE TABLE [Inspector].[DatacollectionsOverdue] ( [ExecutionLogID] INT, [Servername] NVARCHAR(128), [Log_Date] DATETIME, [ModuleConfig_Desc] VARCHAR(20), [Procname] NVARCHAR(128), [DurationInSeconds] INT, [ExecutionDate] DATETIME, [PreviousRunDateTime] DATETIME, [RunNumber] INT, [FrequencyInSeconds] INT, [Variance] INT, [PSCollection] BIT ); END IF NOT EXISTS (SELECT * FROM sys.tables WHERE [name] = N'ReportsDueCache' AND [schema_id] = SCHEMA_ID(N'Inspector')) BEGIN CREATE TABLE [Inspector].[ReportsDueCache] ( [ID] UNIQUEIDENTIFIER NOT NULL, [ModuleConfig_Desc] VARCHAR(20) NOT NULL, [CurrentScheduleStart] DATETIME NOT NULL, [ReportWarningsOnly] TINYINT NOT NULL, [NoClutter] BIT NOT NULL, [Frequency] SMALLINT NOT NULL, [EmailGroup] VARCHAR(50) NULL, [EmailProfile] VARCHAR(128) NULL, [EmailAsAttachment] BIT NULL ); END IF NOT EXISTS (SELECT 1 FROM sys.tables INNER JOIN sys.indexes ON indexes.[object_id] = tables .[object_id] WHERE tables.[name] = N'ReportsDueCache' AND indexes.[name] = N'CIX_ID' AND SCHEMA_NAME(tables.[schema_id]) = N'Inspector') BEGIN CREATE CLUSTERED INDEX [CIX_ID] ON [Inspector].[ReportsDueCache] (ID ASC); END IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE name = N'EmailAsAttachment' AND [object_id] = OBJECT_ID(N'Inspector.ReportsDueCache')) BEGIN ALTER TABLE [Inspector].[ReportsDueCache] ADD [EmailAsAttachment] BIT NULL; END IF NOT EXISTS (SELECT * FROM sys.tables WHERE [name] = N'ExecutionLogLastTruncate' AND [schema_id] = SCHEMA_ID(N'Inspector')) BEGIN CREATE TABLE [Inspector].[ExecutionLogLastTruncate] ( LastTruncate DATE NOT NULL ); END IF NOT EXISTS (SELECT 1 FROM [Inspector].[ExecutionLogLastTruncate]) BEGIN EXEC sp_executesql N'INSERT INTO [Inspector].[ExecutionLogLastTruncate] (LastTruncate) VALUES(CAST(GETDATE() AS DATE));'; END IF OBJECT_ID('Inspector.MonitorHours',N'U') IS NULL BEGIN CREATE TABLE [Inspector].[MonitorHours]( [Servername] [nvarchar](128) NULL, [Modulename] VARCHAR(50) NULL, [MonitorHourStart] INT NOT NULL, [MonitorHourEnd] INT NOT NULL ); EXEC sp_executesql N'CREATE UNIQUE CLUSTERED INDEX [CIX_Servername_Modulename] ON [Inspector].[MonitorHours] ([Servername],[Modulename]);'; END IF OBJECT_ID('Inspector.ServerSettingThresholds',N'U') IS NULL BEGIN CREATE TABLE [Inspector].[ServerSettingThresholds] ( ID INT IDENTITY(1,1), Servername NVARCHAR(128) NOT NULL, Modulename VARCHAR(50) NOT NULL, ThresholdName VARCHAR(100) NOT NULL, ThresholdInt INT NULL, ThresholdString VARCHAR(255) NULL, IsActive BIT NOT NULL ); EXEC sp_executesql N'CREATE UNIQUE CLUSTERED INDEX [ServerSettingThresholds_Servername_Modulename] ON [Inspector].[ServerSettingThresholds] ([Servername],[Modulename],[ThresholdName]);'; END IF OBJECT_ID('Inspector.DriveSpaceThresholds',N'U') IS NULL BEGIN CREATE TABLE [Inspector].[DriveSpaceThresholds]( [Servername] NVARCHAR(128) NULL, [Drive] NVARCHAR(128) NULL, [FreeSpaceRemainingPercent] DECIMAL(5,2) NULL, [MinAvailableSpace_GB] INT NULL, [DaysUntilDriveFull] INT NULL ); EXEC sp_executesql N'CREATE UNIQUE CLUSTERED INDEX [DriveSpaceThresholds_Servername_Drive] ON [Inspector].[DriveSpaceThresholds] ([Servername],[Drive]);'; END IF OBJECT_ID('Inspector.TempDB',N'U') IS NULL BEGIN CREATE TABLE [Inspector].[TempDB] ( Servername NVARCHAR(128) NOT NULL, Log_Date DATETIME NOT NULL, DatabaseFilename NVARCHAR(256) NOT NULL, Reserved_MB DECIMAL(18,2) NOT NULL, Unallocated_MB DECIMAL(18,2) NOT NULL, Internal_object_reserved_MB DECIMAL(18,2) NOT NULL, User_object_reserved_MB DECIMAL(18,2) NOT NULL, Version_store_reserved_MB DECIMAL(18,2) NOT NULL, UsedPct DECIMAL(10,2) NOT NULL, OldestTransactionSessionId INT NULL, OldestTransactionDurationMins DECIMAL(18,2) NULL, TransactionStartTime DATETIME NULL, DateHour AS DATEPART(HOUR,Log_Date) ); EXEC sp_executesql N'CREATE CLUSTERED INDEX [CIX_TempDB_Servername_Log_Date] ON [Inspector].[TempDB] ([Servername] ASC,[Log_Date] ASC,[DateHour] ASC);'; END IF NOT EXISTS(SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'TempDBDataRetentionDays') BEGIN INSERT INTO [Inspector].[Settings] ([Description], [Value]) VALUES('TempDBDataRetentionDays','7'); END IF NOT EXISTS(SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'TempDBPercentUsed') BEGIN INSERT INTO [Inspector].[Settings] ([Description], [Value]) VALUES('TempDBPercentUsed','75'); END IF NOT EXISTS(SELECT 1 FROM [Inspector].[Modules] WHERE [Modulename] = 'TempDB') BEGIN INSERT INTO [Inspector].[Modules] ([ModuleConfig_Desc], [Modulename], [CollectionProcedurename], [ReportProcedurename], [ReportOrder], [WarningLevel], [ServerSpecific], [Debug], [IsActive], [HeaderText], [Frequency], [StartTime], [EndTime], [LastRunDateTime]) VALUES('Default','TempDB','TempDBInsert','TempDBReport',22,2,1,0,1,NULL,5,@StartTime,@EndTime,NULL); END IF NOT EXISTS(SELECT 1 FROM [Inspector].[DefaultHeaderText] WHERE [Modulename] = 'TempDB') BEGIN INSERT INTO [Inspector].[DefaultHeaderText] ([Modulename], [HeaderText]) VALUES('TempDB','TempDB file usage higher than your threshold'); END IF OBJECT_ID('Inspector.MemoryDumps') IS NULL BEGIN CREATE TABLE [Inspector].[MemoryDumps] ( [ID] INT IDENTITY(1,1) NOT NULL, [Servername] NVARCHAR(128) NOT NULL, [filename] NVARCHAR(256) NOT NULL, [creation_time] DATETIMEOFFSET(7) NOT NULL, [size_in_mb] DECIMAL(18,2) NULL ); END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE [object_id] = OBJECT_ID('Inspector.MemoryDumps') AND [name] = N'CIX_MemoryDumps_ID') BEGIN CREATE CLUSTERED INDEX [CIX_MemoryDumps_ID] ON [Inspector].[MemoryDumps] ( [ID] ASC ); END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE [object_id] = OBJECT_ID('Inspector.MemoryDumps') AND [name] = N'IX_MemoryDumps_Servername_creation_time') BEGIN CREATE NONCLUSTERED INDEX [IX_MemoryDumps_Servername_creation_time] ON [Inspector].[MemoryDumps] ( [Servername] ASC, [creation_time] asc ); END IF NOT EXISTS(SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'MemoryDumpsRetentionInDays') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES('MemoryDumpsRetentionInDays','180'); END IF NOT EXISTS(SELECT 1 FROM [Inspector].[Modules] WHERE [Modulename] = 'MemoryDumps') BEGIN INSERT INTO [Inspector].[Modules] ([ModuleConfig_Desc],[Modulename],[CollectionProcedurename],[ReportProcedurename],[ReportOrder],[WarningLevel],[ServerSpecific],[Debug],[IsActive],[HeaderText],[Frequency],[StartTime],[EndTime]) VALUES('Default','MemoryDumps',N'MemoryDumpsInsert',N'MemoryDumpsReport',23,2,1,0,1,NULL,1440,'08:00','17:30'); END IF NOT EXISTS(SELECT 1 FROM [Inspector].[DefaultHeaderText] WHERE [Modulename] = 'MemoryDumps') BEGIN INSERT INTO [Inspector].[DefaultHeaderText] ([Modulename], [HeaderText]) VALUES('MemoryDumps','Recent Memory dumps found'); END IF OBJECT_ID('Inspector.AgentJobsDesiredState') IS NULL BEGIN CREATE TABLE [Inspector].[AgentJobsDesiredState] ( [Servername] NVARCHAR(128) NOT NULL, [JobName] NVARCHAR(128) NOT NULL, [DesiredState] BIT NOT NULL, [Enabled] BIT NOT NULL, [LastChecked] DATETIME NULL ); END IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE [object_id] = OBJECT_ID('Inspector.AgentJobsDesiredState') AND [name] = N'CIX_AgentJobsDesiredState_Servername_JobName') BEGIN CREATE CLUSTERED INDEX [CIX_AgentJobsDesiredState_Servername_JobName] ON [Inspector].[AgentJobsDesiredState] ( [Servername] ASC, [JobName] ASC ); END IF NOT EXISTS(SELECT 1 FROM [Inspector].[Modules] WHERE [Modulename] = 'AgentJobsDesiredState') BEGIN INSERT INTO [Inspector].[Modules] ([ModuleConfig_Desc],[Modulename],[CollectionProcedurename],[ReportProcedurename],[ReportOrder],[WarningLevel],[ServerSpecific],[Debug],[IsActive],[HeaderText],[Frequency],[StartTime],[EndTime]) VALUES('Default','AgentJobsDesiredState',N'AgentJobsDesiredStateInsert',N'AgentJobsDesiredStateReport',24,2,1,0,1,NULL,1440,'08:00','17:30'); END IF NOT EXISTS(SELECT 1 FROM [Inspector].[DefaultHeaderText] WHERE [Modulename] = 'AgentJobsDesiredState') BEGIN INSERT INTO [Inspector].[DefaultHeaderText] ([Modulename], [HeaderText]) VALUES('AgentJobsDesiredState','Agent jobs where desired state not met'); END IF OBJECT_ID('Inspector.PSConfig') IS NULL BEGIN CREATE TABLE [Inspector].[PSConfig]( [Servername] NVARCHAR(128) NOT NULL, [ModuleConfig_Desc] VARCHAR(20) NOT NULL, [Modulename] VARCHAR(50) NOT NULL, [Procedurename] NVARCHAR(128) NULL, [Tablename] NVARCHAR(128) NULL, [StageTablename] NVARCHAR(256) NULL, [StageProcname] NVARCHAR(256) NULL, [TableAction] VARCHAR(3) NOT NULL, [TableAction_Desc] AS CAST(REPLACE(REPLACE(REPLACE([TableAction],'1','Delete All'),'2','Delete with retention'),'3','Stage/Merge') AS VARCHAR(50)), [InsertAction] VARCHAR(3) NOT NULL, [InsertAction_Desc] AS CAST(REPLACE(REPLACE(REPLACE([InsertAction],'1','All data'),'2','Todays data only'),'3','Frequency based') AS VARCHAR(50)), [RetentionInDays] VARCHAR(7) NULL, [IsActive] BIT NOT NULL ); END --V2.1 Change computed column definition IF NOT EXISTS (SELECT * FROM sys.computed_columns WHERE [object_id] = OBJECT_ID('Inspector.PSConfig') AND [name] = N'InsertAction_Desc' AND [definition] LIKE '%Frequency based%') BEGIN ALTER TABLE [Inspector].[PSConfig] DROP COLUMN [InsertAction_Desc]; END IF NOT EXISTS (SELECT * FROM sys.computed_columns WHERE [object_id] = OBJECT_ID('Inspector.PSConfig') AND [name] = N'InsertAction_Desc') BEGIN ALTER TABLE [Inspector].[PSConfig] ADD [InsertAction_Desc] AS CAST(REPLACE(REPLACE(REPLACE([InsertAction],'1','All data'),'2','Todays data only'),'3','Frequency based') AS VARCHAR(50)); END --Insert new settings for V1.2 IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'LongRunningTransactionThreshold') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES ('LongRunningTransactionThreshold',CAST(@LongRunningTransactionThreshold AS VARCHAR(8))); END IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'ReportDataRetention') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES ('ReportDataRetention','30'); END --New setting for V1.4 IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'DataDrives') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES ('DataDrives',@DataDrive); END --New setting for V1.4 IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'LogDrives') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES ('LogDrives',@LogDrive); END --New setting for V1.4 IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'InspectorUpgradeFilenameSync') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES ('InspectorUpgradeFilenameSync',1); END --New Setting for 1.2 - Powershell banner. IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'PSEmailBannerURL') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES ('PSEmailBannerURL','http://bit.ly/PSInspectorEmailBanner'); END --New URL for standard email banner IF (SELECT [Value] FROM [Inspector].[Settings] WHERE [Description] = 'EmailBannerURL') = 'http://bit.ly/InspectorEmailBanner' BEGIN UPDATE [Inspector].[Settings] SET [Value] = 'https://bit.ly/InspectorLogoRev1' WHERE [Description] = 'EmailBannerURL'; END --New URL for standard email banner IF (SELECT [Value] FROM [Inspector].[Settings] WHERE [Description] = 'EmailBannerURL') = 'http://bit.ly/InspectorV2' BEGIN UPDATE [Inspector].[Settings] SET [Value] = 'https://bit.ly/InspectorLogoRev1' WHERE [Description] = 'EmailBannerURL'; END --New Setting for V1.4 IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'AGPrimaryHistoryRetentionPeriodInDays') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES ('AGPrimaryHistoryRetentionPeriodInDays','90'); END ELSE BEGIN UPDATE [Inspector].[Settings] SET [Value] = '90' WHERE [Description] = 'AGPrimaryHistoryRetentionPeriodInDays'; END --New Setting for V2.00 IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'ReportDataDetailedSummary') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES ('ReportDataDetailedSummary','1'); END --New setting for V2.01 IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'BackupSpaceWeekdayOffset') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES('BackupSpaceWeekdayOffset','1'); END --New settings for V2.1 IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'PSAutoUpdateModules') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES('PSAutoUpdateModules','1'); END IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'PSAutoUpdateModulesFrequencyMins') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES('PSAutoUpdateModulesFrequencyMins','1440'); END IF NOT EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'DatabaseGrowthRetentionPeriodInDays') BEGIN INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES('DatabaseGrowthRetentionPeriodInDays',180); END --Update email banner for V2 IF (SELECT [Value] FROM [Inspector].[Settings] WHERE [Description] = 'EmailBannerURL') = 'http://bit.ly/InspectorEmailBanner' BEGIN UPDATE [Inspector].[Settings] SET [Value] = 'https://bit.ly/InspectorLogoRev1' WHERE [Description] = 'EmailBannerURL'; END --Remove linked server settings #277 V2.6 IF EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'CentraliseExecutionLog') BEGIN DELETE FROM [Inspector].[Settings] WHERE [Description] = 'CentraliseExecutionLog'; END IF EXISTS (SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'LinkedServername') BEGIN DELETE FROM [Inspector].[Settings] WHERE [Description] = 'LinkedServername'; END IF (EXISTS(SELECT 1 FROM [Inspector].[Settings] WHERE [Description] = 'DiffBackupThreshold') AND @CurrentBuild < 2.6) BEGIN RAISERROR('Updating your diff threshold from days to hours if required',0,0) WITH NOWAIT; UPDATE [Inspector].[Settings] SET [Value] = [Value]*24 WHERE [Description] = 'DiffBackupThreshold' AND [Value] IS NOT NULL; END --Populate config IF @InitialSetup = 1 BEGIN --Truncate tables - Settings,Modules,EmailConfig,ModuleWarnings,ModuleWarningLevel,EmailRecipients,CatalogueModules EXEC sp_executesql N' TRUNCATE TABLE [Inspector].[Settings]; TRUNCATE TABLE [Inspector].[EmailConfig]; TRUNCATE TABLE [Inspector].[ModuleWarnings]; TRUNCATE TABLE [Inspector].[ModuleWarningLevel]; UPDATE [Inspector].[ModuleConfig] SET [EmailGroup] = NULL WHERE [EmailGroup] IS NOT NULL; UPDATE [Inspector].[ModuleConfig] SET [EmailProfile] = NULL WHERE [EmailProfile] IS NOT NULL; DELETE FROM [Inspector].[EmailRecipients]; TRUNCATE TABLE [Inspector].[CatalogueModules]; TRUNCATE TABLE [Inspector].[DefaultHeaderText]; TRUNCATE TABLE [Inspector].[ServerSettingThresholds];'; UPDATE [Inspector].[CurrentServers] SET ModuleConfig_Desc = NULL WHERE ModuleConfig_Desc IS NOT NULL; DELETE FROM [Inspector].[Modules]; DBCC CHECKIDENT ('Inspector.Modules', RESEED, 0) WITH NO_INFOMSGS; DELETE FROM [Inspector].[ModuleConfig]; DBCC CHECKIDENT ('Inspector.ModuleConfig', RESEED, 0) WITH NO_INFOMSGS; --Insert Settings into Inspector Base tables EXEC sp_executesql N' INSERT INTO [Inspector].[Settings] ([Description],[Value]) VALUES (''SQLUndercoverInspectorEmailSubject'',@StackNameForEmailSubject), (''DriveSpaceRetentionPeriodInDays'',@DriveSpaceHistoryRetentionInDays), (''DatabaseGrowthRetentionPeriodInDays'',''180''), (''AGPrimaryHistoryRetentionPeriodInDays'',''90''), (''FullBackupThreshold'',@FullBackupThreshold), (''DiffBackupThreshold'',@DiffBackupThreshold), (''LogBackupThreshold'' ,@LogBackupThreshold), (''DaysUntilDriveFullThreshold'' ,@DaysUntilDriveFullThreshold), (''FreeSpaceRemainingPercent'',@FreeSpaceRemainingPercent), (''DatabaseGrowthsAllowedPerDay'',@DatabaseGrowthsAllowedPerDay), (''MAXDatabaseGrowthsAllowedPerDay'',@MAXDatabaseGrowthsAllowedPerDay), (''LongRunningTransactionThreshold'',@LongRunningTransactionThreshold), (''ReportDataRetention'',''30''), (''BackupsPath'',@BackupsPath), (''EmailBannerURL'',''https://bit.ly/InspectorLogoRev1''), (''PSEmailBannerURL'',''http://bit.ly/PSInspectorEmailBanner''), (''DatabaseOwnerExclusions'',@DatabaseOwnerExclusions), (''AgentJobOwnerExclusions'',@AgentJobOwnerExclusions), (''InspectorBuild'',@Build), (''DriveSpaceDriveLetterExcludes'',@DriveLetterExcludes), (''DataDrives'',@DataDrive), (''LogDrives'',@LogDrive), (''InspectorUpgradeFilenameSync'',''1''), (''UseMedianCalculationForDriveSpaceCalc'',''0''), (''ReportDataDetailedSummary'',''1''), (''CentraliseExecutionLog'',''0''), (''BackupSpaceWeekdayOffset'',''1''), (''TempDBDataRetentionDays'',''7''), (''TempDBPercentUsed'',''75''), (''LongRunningTransactionsHistoryRetentionDays'',''7''), (''PSAutoUpdateModules'',''1''), (''PSAutoUpdateModulesFrequencyMins'',''1440''), (''MemoryDumpsRetentionInDays'',''180''); IF NOT EXISTS (SELECT 1 FROM [Inspector].[ModuleConfig]) BEGIN INSERT INTO [Inspector].[ModuleConfig] ([ModuleConfig_Desc], [IsActive],[Frequency], [StartTime], [EndTime], [ReportWarningsOnly], [RunDay], [NoClutter],[ShowDisabledModules]) VALUES(''Default'',1,1440,''09:00'',''17:30'',0,''Monday,Tuesday,Wednesday,Thursday,Friday'',0,1), (''PeriodicBackupCheck'',1,120,''11:00'',''17:30'',1,''Monday,Tuesday,Wednesday,Thursday,Friday'',1,1); END INSERT INTO [Inspector].[Modules] ([ModuleConfig_Desc], [Modulename], [CollectionProcedurename], [ReportProcedurename], [ReportOrder], [IsActive], [Debug], [ServerSpecific], [WarningLevel], [HeaderText], [Frequency], [StartTime], [EndTime]) VALUES(''Default'',''ADHocDatabaseCreations'',''ADHocDatabaseCreationsInsert'',''ADHocDatabaseCreationsReport'',1,1,0,1,2,NULL,1440,@StartTime,@EndTime), (''Default'',''AGCheck'',''AGCheckInsert'',''AGCheckReport'',2,1,0,1,1,NULL,1440,@StartTime,@EndTime), (''Default'',''AGDatabases'',''AGDatabasesInsert'',''AGDatabasesReport'',3,1,0,1,2,NULL,1440,@StartTime,@EndTime), (''Default'',''BackupsCheck'',''BackupsCheckInsert'',''BackupsCheckReport'',4,1,0,1,1,NULL,1440,@StartTime,@EndTime), (''Default'',''BackupSizesByDay'',''BackupSizesByDayInsert'',''BackupSizesByDayReport'',5,1,0,1,3,NULL,1440,@StartTime,@EndTime), (''Default'',''BackupSpace'',''BackupSpaceInsert'',''BackupSpaceReport'',6,1,0,0,1,NULL,1440,@StartTime,@EndTime), (''Default'',''DatabaseFiles'',''DatabaseFilesInsert'',''DatabaseFilesReport'',7,1,0,1,2,NULL,1440,@StartTime,@EndTime), (''Default'',''DatabaseGrowths'',''DatabaseGrowthsInsert'',''DatabaseGrowthsReport'',8,1,0,0,2,NULL,1440,@StartTime,@EndTime), (''Default'',''DatabaseOwnership'',''DatabaseOwnershipInsert'',''DatabaseOwnershipReport'',9,1,0,1,3,NULL,1440,@StartTime,@EndTime), (''Default'',''DatabaseSettings'',''DatabaseSettingsInsert'',''DatabaseSettingsReport'',10,1,0,1,3,NULL,1440,@StartTime,@EndTime), (''Default'',''DatabaseStates'',''DatabaseStatesInsert'',''DatabaseStatesReport'',11,1,0,1,2,NULL,1440,@StartTime,@EndTime), (''Default'',''DriveSpace'',''DriveSpaceInsert'',''DriveSpaceReport'',1,1,0,1,1,NULL,1440,@StartTime,@EndTime), (''Default'',''FailedAgentJobs'',''FailedAgentJobsInsert'',''FailedAgentJobsReport'',13,1,0,1,2,NULL,1440,@StartTime,@EndTime), (''Default'',''JobOwner'',''JobOwnerInsert'',''JobOwnerReport'',15,1,0,1,3,NULL,1440,@StartTime,@EndTime), (''Default'',''LoginAttempts'',''LoginAttemptsInsert'',''LoginAttemptsReport'',14,1,0,1,2,NULL,1440,@StartTime,@EndTime), (''Default'',''LongRunningTransactions'',''LongRunningTransactionsInsert'',''LongRunningTransactionsReport'',16,1,0,1,3,NULL,1440,@StartTime,@EndTime), (''Default'',''ServerSettings'',''ServerSettingsInsert'',''ServerSettingsReport'',17,1,0,1,2,NULL,1440,@StartTime,@EndTime), (''Default'',''SuspectPages'',''SuspectPagesInsert'',''SuspectPagesReport'',1,1,0,1,1,NULL,1440,@StartTime,@EndTime), (''Default'',''TopFiveDatabases'',''TopFiveDatabasesInsert'',''TopFiveDatabasesReport'',19,1,0,1,3,NULL,1440,@StartTime,@EndTime), (''Default'',''UnusedLogshipConfig'',''UnusedLogshipConfigInsert'',''UnusedLogshipConfigReport'',20,1,0,1,3,NULL,1440,@StartTime,@EndTime), (''Default'',''DatacollectionsOverdue'',''DatacollectionsOverdueInsert'',''DatacollectionsOverdueReport'',21,1,0,0,1,NULL,1440,@StartTime,@EndTime), (''Default'',''TempDB'',''TempDBInsert'',''TempDBReport'',22,2,1,0,1,NULL,5,@StartTime,@EndTime), (''PeriodicBackupCheck'',''BackupsCheck'',''BackupsCheckInsert'',''BackupsCheckReport'',1,1,0,1,1,NULL,120,DATEADD(HOUR,2,@StartTime),@EndTime), (''Default'',''MemoryDumps'',N''MemoryDumpsInsert'',N''MemoryDumpsReport'',23,2,1,0,1,NULL,1440,@StartTime,@EndTime), (''Default'',''AgentJobsDesiredState'',N''AgentJobsDesiredStateInsert'',N''AgentJobsDesiredStateReport'',24,2,1,0,1,NULL,1440,@StartTime,@EndTime); INSERT INTO [Inspector].[DefaultHeaderText] ([Modulename], [HeaderText]) VALUES(''ADHocDatabaseCreations'',''Potential ADhoc database creations''), (''AGCheck'',''AG Warnings''), (''AGDatabases'',''Databases not joined to an Availability group''), (''BackupsCheck '',''Database Backup issues''), (''BackupSpace'',''Backup space issues''), (''CatalogueDroppedDatabases'',''Dropped databases''), (''CatalogueDroppedTables'',''Dropped tables''), (''CatalogueMissingLogins'',''Missing Logins''), (''DatabaseFiles'',''Database files on incorrect drives''), (''DatabaseGrowths'',''Database Growths in the last 24 hours exceeding thresholds''), (''DatabaseOwnership'',''Database ownerships not set to your preferred owner''), (''DatabaseSettings'',''Database Auto Close or Auto Shrink settings enabled or Auto Update Stats Disabled''), (''DatabaseStates'',''Database state warnings''), (''FailedAgentJobs'',''Failed agent jobs''), (''LoginAttempts'',''Failed logins''), (''JobOwner'',''Agent jobs not set to your preferred owner''), (''LongRunningTransactions'',''Long running transactions exceeding your threshold''), (''ServerSettings'',''Cost Threshold for parallelism, MAXDOP or Max Server memory set to default values''), (''SuspectPages'',''Suspect database pages found''), (''UnusedLogshipConfig'',''Unused log shipping config found''), (''TempDB'',''TempDB file usage higher than your threshold''), (''MemoryDumps'',''Recent Memory dumps found''), (''AgentJobsDesiredState'',''Agent jobs where desired state not met''); INSERT INTO [Inspector].[EmailConfig] (ModuleConfig_Desc,EmailSubject) VALUES (''Default'',''SQLUndercover Inspector check ''),(''PeriodicBackupCheck'',''SQLUndercover Backups Report''); INSERT INTO [Inspector].[ModuleWarnings] ([WarningLevel],[WarningDesc]) VALUES(NULL,''InspectorDefault''),(1,''Red''),(2,''Yellow''),(3,''Information (white)'');', N'@StackNameForEmailSubject VARCHAR(255), @EmailRecipientList VARCHAR(1000), @DriveSpaceHistoryRetentionInDays VARCHAR(6), @FullBackupThreshold VARCHAR(3), @DiffBackupThreshold VARCHAR(3), @LogBackupThreshold VARCHAR(6), @DaysUntilDriveFullThreshold VARCHAR(4), @FreeSpaceRemainingPercent VARCHAR(3), @DatabaseGrowthsAllowedPerDay VARCHAR(5), @MAXDatabaseGrowthsAllowedPerDay VARCHAR(5), @LongRunningTransactionThreshold VARCHAR(8), @BackupsPath VARCHAR(255), @DatabaseOwnerExclusions VARCHAR(255), @AgentJobOwnerExclusions VARCHAR(255), @DriveLetterExcludes VARCHAR(10), @DataDrive VARCHAR(50), @LogDrive VARCHAR(50), @Build VARCHAR(6), @StartTime TIME(0), @EndTime TIME(0)', @StackNameForEmailSubject = @StackNameForEmailSubject, @EmailRecipientList = @EmailRecipientList, @DriveSpaceHistoryRetentionInDays = @DriveSpaceHistoryRetentionInDays, @FullBackupThreshold = @FullBackupThreshold, @DiffBackupThreshold = @DiffBackupThreshold, @LogBackupThreshold = @LogBackupThreshold, @DaysUntilDriveFullThreshold = @DaysUntilDriveFullThreshold, @FreeSpaceRemainingPercent = @FreeSpaceRemainingPercent, @DatabaseGrowthsAllowedPerDay = @DatabaseGrowthsAllowedPerDay, @MAXDatabaseGrowthsAllowedPerDay = @MAXDatabaseGrowthsAllowedPerDay, @LongRunningTransactionThreshold = @LongRunningTransactionThreshold, @BackupsPath = @BackupsPath, @DatabaseOwnerExclusions = @DatabaseOwnerExclusions, @AgentJobOwnerExclusions = @AgentJobOwnerExclusions, @DriveLetterExcludes = @DriveLetterExcludes, @DataDrive = @DataDrive, @LogDrive = @LogDrive, @Build = @Build, @StartTime = @StartTime, @EndTime = @EndTime; SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' IF SERVERPROPERTY(''IsHadrEnabled'') = 1 AND EXISTS (SELECT name FROM sys.availability_groups) BEGIN INSERT INTO [Inspector].[CurrentServers] (Servername,IsActive,ModuleConfig_Desc) SELECT DISTINCT replica_server_name,0,NULL FROM sys.dm_hadr_availability_replica_cluster_nodes AGServers WHERE NOT EXISTS (SELECT Servername FROM [Inspector].[CurrentServers] WHERE Servername COLLATE DATABASE_DEFAULT = AGServers.replica_server_name) END ELSE BEGIN INSERT INTO [Inspector].[CurrentServers] (Servername,IsActive,ModuleConfig_Desc) SELECT @@SERVERNAME,1,NULL WHERE NOT EXISTS (SELECT Servername FROM [Inspector].[CurrentServers] WHERE Servername = @@Servername) END ' EXEC(@SQLStatement); IF @EmailRecipientList IS NULL BEGIN EXEC sp_executesql N' INSERT INTO [Inspector].[EmailRecipients] (Description) VALUES (''DBA'');'; END ELSE BEGIN EXEC sp_executesql N' INSERT INTO [Inspector].[EmailRecipients] (Description,Recipients) VALUES (''DBA'',@EmailRecipientList);',N'@EmailRecipientList VARCHAR(1000)',@EmailRecipientList = @EmailRecipientList; END END IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[GetIntervals]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) BEGIN EXECUTE dbo.sp_executesql @statement = N'CREATE FUNCTION [Inspector].[GetIntervals](@top AS BIGINT) RETURNS TABLE AS RETURN WITH L0 AS (SELECT c FROM (SELECT 1 UNION ALL SELECT 1) AS D(c)), L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B), L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B), L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B), L4 AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B), L5 AS (SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B), Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1 AS RowNum FROM L5) SELECT TOP(@top) RowNum FROM Nums ORDER BY RowNum; ' END IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[GetIntervals]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) BEGIN EXECUTE dbo.sp_executesql @statement = N'ALTER FUNCTION [Inspector].[GetIntervals](@top AS BIGINT) RETURNS TABLE AS RETURN WITH L0 AS (SELECT c FROM (SELECT 1 UNION ALL SELECT 1) AS D(c)), L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B), L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B), L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B), L4 AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B), L5 AS (SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B), Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1 AS RowNum FROM L5) SELECT TOP(@top) RowNum FROM Nums ORDER BY RowNum; ' END IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[GetServerModuleThreshold]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) BEGIN EXECUTE dbo.sp_executesql @statement = N'CREATE FUNCTION [Inspector].[GetServerModuleThreshold] (@Servername NVARCHAR(128)) RETURNS VARCHAR(255) AS BEGIN DECLARE @ThresholdValue VARCHAR(20); RETURN @ThresholdValue; END'; END EXECUTE dbo.sp_executesql @statement = N'ALTER FUNCTION [Inspector].[GetServerModuleThreshold] ( @Servername NVARCHAR(128), @ModuleName VARCHAR(100), @SettingName VARCHAR(100) ) RETURNS VARCHAR(255) AS BEGIN DECLARE @ThresholdValue VARCHAR(255); SELECT @ThresholdValue = CASE /* If there is an override then use it even if it is a NULL value as this may be intended */ WHEN [ServerSettingThresholds].[Modulename] IS NOT NULL THEN ISNULL(CAST([ThresholdInt] AS VARCHAR(255)),[ThresholdString]) ELSE [GlobalSettings].[Value] END FROM ( SELECT [Servername], [Description] AS ThresholdName, [Value] FROM [Inspector].[Settings] CROSS JOIN (SELECT [Servername] FROM [Inspector].[CurrentServers] WHERE [IsActive] = 1) ActiveServers WHERE [Description] = @SettingName ) GlobalSettings LEFT JOIN (SELECT [Servername], [Modulename], [ThresholdName], [ThresholdInt], [ThresholdString] FROM [Inspector].[ServerSettingThresholds] WHERE [IsActive] = 1) [ServerSettingThresholds] ON GlobalSettings.[Servername] = ServerSettingThresholds.[Servername] AND GlobalSettings.[ThresholdName] = ServerSettingThresholds.[ThresholdName] WHERE (ServerSettingThresholds.[Modulename] = @ModuleName OR ServerSettingThresholds.[Modulename] IS NULL) AND GlobalSettings.[Servername] = @Servername; RETURN @ThresholdValue; END'; IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[GetLastCollectionDateTime]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) BEGIN execute dbo.sp_executesql @statement = N' CREATE FUNCTION [Inspector].[GetLastCollectionDateTime] ( @Modulename VARCHAR(50) ) RETURNS DATETIME AS --Revision date: 29/03/2021 BEGIN DECLARE @LastDateTime DATETIME; /* We are not filtering on ModuleConfig_Desc here because you might be sharing collections */ SET @LastDateTime = (SELECT TOP 1 [LastRunDateTime] FROM [Inspector].[Modules] WHERE [Modulename] = @Modulename ORDER BY [LastRunDateTime] DESC); RETURN(ISNULL(@LastDateTime,''19000101'')); END ' END; IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[GetLastReportDateTime]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) BEGIN execute dbo.sp_executesql @statement = N' CREATE FUNCTION [Inspector].[GetLastReportDateTime] ( @ModuleConfig VARCHAR(20) ) RETURNS DATETIME AS --Revision date: 29/03/2021 BEGIN DECLARE @LastDateTime DATETIME; SET @LastDateTime = (SELECT [LastRunDateTime] FROM [Inspector].[ModuleConfig] WHERE [ModuleConfig_Desc] = @ModuleConfig); RETURN(ISNULL(@LastDateTime,CAST(CAST(GETDATE() AS DATE) AS DATETIME))); END ' END; IF NOT EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[Inspector].[DriveSpaceCapacityInfo]')) EXEC dbo.sp_executesql @statement = N'CREATE VIEW [Inspector].[DriveSpaceCapacityInfo] AS SELECT 1 AS A;'; EXEC dbo.sp_executesql @statement = N'ALTER VIEW [Inspector].[DriveSpaceCapacityInfo] AS SELECT Servername, Log_Date, Drive, Capacity_GB, AvailableSpace_GB, DATEDIFF(DAY,LEAD(Log_Date,1,Log_Date) OVER(Partition by Servername,Drive ORDER BY Log_Date DESC),Log_Date) AS DaysSinceCapacityChange, Capacity_GB-LEAD(Capacity_GB,1,Capacity_GB) OVER(Partition by Servername,Drive ORDER BY Log_Date DESC) AS CapacityChange FROM ( SELECT Servername, Log_Date, Drive, Capacity_GB, AvailableSpace_GB, ROW_NUMBER() OVER(Partition by Capacity_GB,Servername,Drive ORDER BY Log_Date DESC) as CapacityChange FROM [Inspector].[DriveSpace] ) CapacityChanges WHERE CapacityChange = 1;'; IF NOT EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[Inspector].[ExecutionInfo]')) EXEC dbo.sp_executesql @statement = N'CREATE VIEW [Inspector].[ExecutionInfo] AS SELECT 1 AS A'; EXEC dbo.sp_executesql @statement = N'ALTER VIEW [Inspector].[ExecutionInfo] AS SELECT Procname, COUNT(Procname) AS ExecutionCount, SUM(Duration) AS TotalDuration_Seconds, AVG(Duration) AS AverageDuration_Seconds, MAX(Duration) AS MaxDuration_Seconds FROM Inspector.ExecutionLog WHERE Procname != N''InspectorDataCollection'' GROUP BY Procname;'; IF NOT EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[Inspector].[ModuleSchedulesDue]')) BEGIN EXEC dbo.sp_executesql @statement = N'CREATE VIEW [Inspector].[ModuleSchedulesDue] AS --Revision date: 13/03/2020 SELECT [Schedules].[Modulename], [Schedules].[CollectionProcedurename], [ModuleConfig_Desc], Frequency, StartDatetime, EndDatetime, DATEADD(MINUTE,RowNum,Schedules.StartDatetime) AS CurrentScheduleStart, DATEADD(MINUTE,RowNum+1,Schedules.StartDatetime) AS CurrentScheduleEnd, LastRunDateTime, RowNum%Frequency AS modulo, RowNum FROM ( SELECT [ModuleConfig_Desc], [Modulename], [CollectionProcedurename], [Frequency], (DATEDIFF(HOUR,CAST(StartTime AS DATETIME),CAST(EndTime AS DATETIME))*60)/Frequency AS TotalRuns, DATEADD(MINUTE,DATEPART(MINUTE,StartTime),DATEADD(HOUR,DATEPART(HOUR,StartTime),CAST(CAST(GETDATE() AS DATE) AS DATETIME))) AS StartDatetime, DATEADD(MINUTE,DATEPART(MINUTE,EndTime),DATEADD(HOUR,DATEPART(HOUR,EndTime),CAST(CAST(GETDATE() AS DATE) AS DATETIME))) AS EndDatetime, LastRunDateTime FROM ( SELECT [ModuleConfig_Desc],[Modulename],CollectionProcedurename,Frequency,StartTime,EndTime,LastRunDateTime FROM [Inspector].[Modules] WHERE [IsActive] = 1 AND CollectionProcedurename IS NOT NULL ) Modules WHERE CAST(GETDATE() AS TIME(0)) >= StartTime AND CAST(GETDATE() AS TIME(0)) <= EndTime ) AS Schedules CROSS APPLY (SELECT RowNum FROM [Inspector].GetIntervals(DATEDIFF(MINUTE,Schedules.StartDatetime,Schedules.EndDatetime))) AS MinuteIntervals WHERE --Check the that he time now falls into a scheduled interval ((GETDATE() >= DATEADD(MINUTE,RowNum,Schedules.StartDatetime) AND GETDATE() <= DATEADD(MINUTE,RowNum+1,Schedules.StartDatetime))) ----Check that the current interval (if there is one) is exactly divisible by the Frequency AND ((RowNum%Frequency = 0) --Check if no run has occured (NULL) OR if the last run is before the start time for today OR (LastRunDateTime IS NULL OR LastRunDateTime < Schedules.StartDatetime))' END ELSE BEGIN EXEC dbo.sp_executesql @statement = N'ALTER VIEW [Inspector].[ModuleSchedulesDue] AS --Revision date: 13/04/2020 SELECT [Schedules].[Modulename], [Schedules].[CollectionProcedurename], [ModuleConfig_Desc], Frequency, StartDatetime, EndDatetime, DATEADD(MINUTE,RowNum,Schedules.StartDatetime) AS CurrentScheduleStart, DATEADD(MINUTE,RowNum+1,Schedules.StartDatetime) AS CurrentScheduleEnd, LastRunDateTime, RowNum%Frequency AS modulo, RowNum FROM ( SELECT [ModuleConfig_Desc], [Modulename], [CollectionProcedurename], [Frequency], (DATEDIFF(HOUR,CAST(StartTime AS DATETIME),CAST(EndTime AS DATETIME))*60)/Frequency AS TotalRuns, DATEADD(MINUTE,DATEPART(MINUTE,StartTime),DATEADD(HOUR,DATEPART(HOUR,StartTime),CAST(CAST(GETDATE() AS DATE) AS DATETIME))) AS StartDatetime, DATEADD(MINUTE,DATEPART(MINUTE,EndTime),DATEADD(HOUR,DATEPART(HOUR,EndTime),CAST(CAST(GETDATE() AS DATE) AS DATETIME))) AS EndDatetime, LastRunDateTime FROM ( SELECT [ModuleConfig_Desc],[Modulename],CollectionProcedurename,Frequency,StartTime,EndTime,LastRunDateTime FROM [Inspector].[Modules] WHERE [IsActive] = 1 AND CollectionProcedurename IS NOT NULL ) Modules WHERE CAST(GETDATE() AS TIME(0)) >= StartTime AND CAST(GETDATE() AS TIME(0)) <= EndTime ) AS Schedules CROSS APPLY (SELECT RowNum FROM [Inspector].GetIntervals(DATEDIFF(MINUTE,Schedules.StartDatetime,Schedules.EndDatetime))) AS MinuteIntervals WHERE --Check the that he time now falls into a scheduled interval ((GETDATE() >= DATEADD(MINUTE,RowNum,Schedules.StartDatetime) AND GETDATE() <= DATEADD(MINUTE,RowNum+1,Schedules.StartDatetime))) ----Check that the current interval (if there is one) is exactly divisible by the Frequency AND ((RowNum%Frequency = 0) --Check if no run has occured (NULL) OR if the last run is before the start time for today OR (LastRunDateTime IS NULL OR LastRunDateTime < Schedules.StartDatetime))' END IF NOT EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[Inspector].[ReportSchedulesDue]')) BEGIN EXEC dbo.sp_executesql @statement = N' CREATE VIEW [Inspector].[ReportSchedulesDue] AS --Revision date: 15/05/2020 SELECT [ModuleConfig_Desc], ReportWarningsOnly, NoClutter, Frequency, StartDatetime, EndDatetime, DATEADD(MINUTE,RowNum,Schedules.StartDatetime) AS CurrentScheduleStart, DATEADD(MINUTE,RowNum+1,Schedules.StartDatetime) AS CurrentScheduleEnd, LastRunDateTime, RowNum%Frequency AS modulo, RowNum, EmailGroup, EmailProfile, EmailAsAttachment FROM ( SELECT [ModuleConfig_Desc], [Frequency], [ReportWarningsOnly], [NoClutter], (DATEDIFF(HOUR,CAST(StartTime AS DATETIME),CAST(EndTime AS DATETIME))*60)/Frequency AS TotalRuns, DATEADD(MINUTE,DATEPART(MINUTE,StartTime),DATEADD(HOUR,DATEPART(HOUR,StartTime),CAST(CAST(GETDATE() AS DATE) AS DATETIME))) AS StartDatetime, DATEADD(MINUTE,DATEPART(MINUTE,EndTime),DATEADD(HOUR,DATEPART(HOUR,EndTime),CAST(CAST(GETDATE() AS DATE) AS DATETIME))) AS EndDatetime, LastRunDateTime,RunDay, ISNULL(EmailGroup,''DBA'') AS EmailGroup, EmailProfile, EmailAsAttachment FROM ( SELECT [ModuleConfig_Desc],Frequency,StartTime,EndTime,LastRunDateTime,ReportWarningsOnly,NoClutter,RunDay,EmailGroup,EmailProfile,ISNULL(EmailAsAttachment,0) AS EmailAsAttachment FROM [Inspector].[ModuleConfig] WHERE [IsActive] = 1 ) Modules WHERE CAST(GETDATE() AS TIME(0)) >= StartTime AND CAST(GETDATE() AS TIME(0)) <= EndTime ) AS Schedules CROSS APPLY (SELECT RowNum FROM [Inspector].GetIntervals(DATEDIFF(MINUTE,Schedules.StartDatetime,Schedules.EndDatetime))) AS MinuteIntervals WHERE --RunDay (delimited) is like today''s day name or RunDay is not specified (Every day) (RunDay LIKE ''%''+CAST(DATENAME(WEEKDAY,GETDATE()) AS VARCHAR(10))+''%'' OR RunDay IS NULL) --Check the that the time now falls into a scheduled interval AND ((GETDATE() >= DATEADD(MINUTE,RowNum,Schedules.StartDatetime) AND GETDATE() <= DATEADD(MINUTE,RowNum+1,Schedules.StartDatetime))) ----Check that the current interval (if there is one) is exactly divisible by the Frequency AND ((RowNum%Frequency = 0) --Check if no run has occured (NULL) OR if the last run is before the start time for today OR (LastRunDateTime IS NULL OR LastRunDateTime < Schedules.StartDatetime))' END ELSE BEGIN EXEC dbo.sp_executesql @statement = N' ALTER VIEW [Inspector].[ReportSchedulesDue] AS --Revision date: 15/05/2020 SELECT [ModuleConfig_Desc], ReportWarningsOnly, NoClutter, Frequency, StartDatetime, EndDatetime, DATEADD(MINUTE,RowNum,Schedules.StartDatetime) AS CurrentScheduleStart, DATEADD(MINUTE,RowNum+1,Schedules.StartDatetime) AS CurrentScheduleEnd, LastRunDateTime, RowNum%Frequency AS modulo, RowNum, EmailGroup, EmailProfile, EmailAsAttachment FROM ( SELECT [ModuleConfig_Desc], [Frequency], [ReportWarningsOnly], [NoClutter], (DATEDIFF(HOUR,CAST(StartTime AS DATETIME),CAST(EndTime AS DATETIME))*60)/Frequency AS TotalRuns, DATEADD(MINUTE,DATEPART(MINUTE,StartTime),DATEADD(HOUR,DATEPART(HOUR,StartTime),CAST(CAST(GETDATE() AS DATE) AS DATETIME))) AS StartDatetime, DATEADD(MINUTE,DATEPART(MINUTE,EndTime),DATEADD(HOUR,DATEPART(HOUR,EndTime),CAST(CAST(GETDATE() AS DATE) AS DATETIME))) AS EndDatetime, LastRunDateTime,RunDay, ISNULL(EmailGroup,''DBA'') AS EmailGroup, EmailProfile, EmailAsAttachment FROM ( SELECT [ModuleConfig_Desc],Frequency,StartTime,EndTime,LastRunDateTime,ReportWarningsOnly,NoClutter,RunDay,EmailGroup,EmailProfile,ISNULL(EmailAsAttachment,0) AS EmailAsAttachment FROM [Inspector].[ModuleConfig] WHERE [IsActive] = 1 ) Modules WHERE CAST(GETDATE() AS TIME(0)) >= StartTime AND CAST(GETDATE() AS TIME(0)) <= EndTime ) AS Schedules CROSS APPLY (SELECT RowNum FROM [Inspector].GetIntervals(DATEDIFF(MINUTE,Schedules.StartDatetime,Schedules.EndDatetime))) AS MinuteIntervals WHERE --RunDay (delimited) is like today''s day name or RunDay is not specified (Every day) (RunDay LIKE ''%''+CAST(DATENAME(WEEKDAY,GETDATE()) AS VARCHAR(10))+''%'' OR RunDay IS NULL) --Check the that the time now falls into a scheduled interval AND ((GETDATE() >= DATEADD(MINUTE,RowNum,Schedules.StartDatetime) AND GETDATE() <= DATEADD(MINUTE,RowNum+1,Schedules.StartDatetime))) ----Check that the current interval (if there is one) is exactly divisible by the Frequency AND ((RowNum%Frequency = 0) --Check if no run has occured (NULL) OR if the last run is before the start time for today OR (LastRunDateTime IS NULL OR LastRunDateTime < Schedules.StartDatetime))' END IF NOT EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'Inspector.ExecutionLogDetails')) BEGIN EXEC dbo.sp_executesql @statement = N'CREATE VIEW [Inspector].[ExecutionLogDetails] AS --Revision date: 31/10/2019 SELECT TOP (2147483647) --Crazy top so that we can retain the Order by clause. ID, Servername, ModuleConfig_Desc, Procname, Frequency, Duration, ExecutionDate, PreviousRunDateTime, Rownum AS RunNumber, CASE WHEN Rownum = 1 THEN Frequency ELSE ActualFrequency END AS ActualFrequency, PSCollection FROM ( SELECT ExecutionLog.ID, ExecutionLog.Servername, ExecutionLog.ModuleConfig_Desc, ExecutionLog.Procname, CASE WHEN ExecutionLog.Procname LIKE ''%Report'' THEN (SELECT Frequency FROM Inspector.ModuleConfig WHERE [ModuleConfig_Desc] = ExecutionLog.ModuleConfig_Desc) ELSE ExecutionLog.Frequency END AS Frequency, Duration, ExecutionDate, LAG(ExecutionDate,1,ExecutionDate) OVER (PARTITION BY ExecutionLog.ModuleConfig_Desc,ExecutionLog.Procname,ExecutionLog.Servername ORDER BY ExecutionLog.ExecutionDate ASC) AS PreviousRunDateTime, ROW_NUMBER() OVER (PARTITION BY ExecutionLog.ModuleConfig_Desc,ExecutionLog.Procname,ExecutionLog.Servername ORDER BY ExecutionLog.ExecutionDate ASC) AS Rownum, DATEDIFF(MINUTE,LAG(ExecutionDate,1,ExecutionDate) OVER (PARTITION BY ExecutionLog.ModuleConfig_Desc,ExecutionLog.Procname,ExecutionLog.Servername ORDER BY ExecutionLog.ExecutionDate ASC),ExecutionDate) AS ActualFrequency, PSCollection FROM [Inspector].[Modules] RIGHT JOIN [Inspector].[ExecutionLog] ON Modules.ModuleConfig_Desc = ExecutionLog.ModuleConfig_Desc AND CollectionProcedurename = Procname ) AS ModuleExecutions ORDER BY ModuleConfig_Desc ASC, Procname ASC, Servername ASC, ExecutionDate ASC' END ELSE BEGIN EXEC dbo.sp_executesql @statement = N'ALTER VIEW [Inspector].[ExecutionLogDetails] AS --Revision date: 31/10/2019 SELECT TOP (2147483647) --Crazy top so that we can retain the Order by clause. ID, Servername, ModuleConfig_Desc, Procname, Frequency, Duration, ExecutionDate, PreviousRunDateTime, Rownum AS RunNumber, CASE WHEN Rownum = 1 THEN Frequency ELSE ActualFrequency END AS ActualFrequency, PSCollection FROM ( SELECT ExecutionLog.ID, ExecutionLog.Servername, ExecutionLog.ModuleConfig_Desc, ExecutionLog.Procname, CASE WHEN ExecutionLog.Procname LIKE ''%Report'' THEN (SELECT Frequency FROM Inspector.ModuleConfig WHERE [ModuleConfig_Desc] = ExecutionLog.ModuleConfig_Desc) ELSE ExecutionLog.Frequency END AS Frequency, Duration, ExecutionDate, LAG(ExecutionDate,1,ExecutionDate) OVER (PARTITION BY ExecutionLog.ModuleConfig_Desc,ExecutionLog.Procname,ExecutionLog.Servername ORDER BY ExecutionLog.ExecutionDate ASC) AS PreviousRunDateTime, ROW_NUMBER() OVER (PARTITION BY ExecutionLog.ModuleConfig_Desc,ExecutionLog.Procname,ExecutionLog.Servername ORDER BY ExecutionLog.ExecutionDate ASC) AS Rownum, DATEDIFF(MINUTE,LAG(ExecutionDate,1,ExecutionDate) OVER (PARTITION BY ExecutionLog.ModuleConfig_Desc,ExecutionLog.Procname,ExecutionLog.Servername ORDER BY ExecutionLog.ExecutionDate ASC),ExecutionDate) AS ActualFrequency, PSCollection FROM [Inspector].[Modules] RIGHT JOIN [Inspector].[ExecutionLog] ON Modules.ModuleConfig_Desc = ExecutionLog.ModuleConfig_Desc AND CollectionProcedurename = Procname ) AS ModuleExecutions ORDER BY ModuleConfig_Desc ASC, Procname ASC, Servername ASC, ExecutionDate ASC' END IF OBJECT_ID('Inspector.PowerBIBackupsview') IS NULL BEGIN EXEC sp_executesql N'CREATE VIEW [Inspector].[PowerBIBackupsview] AS --Revision Date: 01/05/2019 WITH RawData AS (SELECT Log_Date, LTRIM(RTRIM(BackupSet.Databasename)) AS Databasename, --Added trim as Leading and trailing spaces can cause misreporting [FULL] AS LastFull, [DIFF] AS LastDiff, [LOG] AS LastLog, BackupSet.AGname, CASE WHEN BackupSet.AGname = ''Not in an AG'' THEN BackupSet.Servername ELSE BackupSet.AGname END AS GroupingMethod, BackupSet.Servername, BackupSet.IsFullRecovery, BackupSet.IsSystemDB, BackupSet.primary_replica, BackupSet.backup_preference FROM [Inspector].[BackupsCheck] BackupSet INNER JOIN [Inspector].[CurrentServers] ON BackupSet.Servername = CurrentServers.Servername WHERE CAST(GETDATE() AS DATE) >= CAST(GETDATE() AS DATE) AND CurrentServers.IsActive = 1 AND NOT EXISTS (SELECT 1 FROM [Inspector].[BackupsCheckExcludes] WHERE [Servername] = [BackupSet].[Servername] AND [Databasename] = [BackupSet].[Databasename] AND ([SuppressUntil] IS NULL OR [SuppressUntil] >= GETDATE()) ) ), Aggregates AS ( SELECT MAX(Log_Date) AS Log_Date, RawData.Databasename, MAX(Servername) AS Servername, MAX(LastFull) AS LastFull, MAX(LastDiff) AS LastDiff, MAX(LastLog) AS LastLog, AGname, GroupingMethod, IsFullRecovery, IsSystemDB, MAX(primary_replica) AS primary_replica, UPPER(backup_preference) AS backup_preference FROM RawData RawData GROUP BY Databasename,AGname,GroupingMethod,IsFullRecovery,IsSystemDB,backup_preference ), Validations AS ( SELECT Log_Date, Databasename, Servername, LastFull, LastDiff, LastLog, DATEDIFF(DAY,LastFull,Log_Date) AS FullBackupAge, DATEDIFF(DAY,LastDiff,Log_Date) AS DiffBackupAge, DATEDIFF(MINUTE,LastLog,Log_Date) AS LogBackupAge, AGname, GroupingMethod, IsFullRecovery, IsSystemDB, primary_replica, backup_preference, (SELECT ISNULL(CAST([Value] AS INT),8) FROM [Inspector].[Settings] WHERE [Description] = ''FullBackupThreshold'') AS FullBackupThreshold, (SELECT ISNULL(CAST([Value] AS INT),365) FROM [Inspector].[Settings] WHERE [Description] = ''DiffBackupThreshold'') AS DiffBackupThreshold, (SELECT ISNULL(CAST([Value] AS INT),60) FROM [Inspector].[Settings] WHERE [Description] = ''LogBackupThreshold'') AS LogBackupThreshold FROM Aggregates ) SELECT Log_Date, Databasename, Servername, NULLIF(LastFull,''19000101 00:00:00'') AS LastFull, NULLIF(LastDiff,''19000101 00:00:00'') AS LastDiff, NULLIF(LastLog,''19000101 00:00:00'') AS LastLog, FullBackupAge, DiffBackupAge, LogBackupAge, REPLACE(AGname,''Not in an AG'',''Non AG'') AS AGname, GroupingMethod, IsFullRecovery, IsSystemDB, primary_replica, backup_preference, FullBackupThreshold, DiffBackupThreshold, LogBackupThreshold, CASE WHEN FullBackupAge > FullBackupThreshold THEN 1 ELSE 0 END AS FullBackupBreach, CASE WHEN IsSystemDB = 1 THEN 0 WHEN IsSystemDB = 0 AND (SELECT [Value] FROM [Inspector].[Settings] WHERE [Description] = ''DiffBackupThreshold'') IS NULL THEN 0 WHEN IsSystemDB = 0 AND (SELECT [Value] FROM [Inspector].[Settings] WHERE [Description] = ''DiffBackupThreshold'') IS NOT NULL AND DiffBackupAge > DiffBackupThreshold THEN 1 ELSE 0 END AS DiffBackupBreach, CASE WHEN IsSystemDB = 1 OR IsFullRecovery = 0 THEN 0 WHEN (IsSystemDB = 0 AND IsFullRecovery = 1) AND LogBackupAge > LogBackupThreshold THEN 1 ELSE 0 END AS LogBackupBreach, CASE WHEN IsSystemDB = 1 OR IsFullRecovery = 0 THEN 1 WHEN (IsSystemDB = 0 AND IsFullRecovery = 1) AND (SELECT [Value] FROM [Inspector].[Settings] WHERE [Description] = ''DiffBackupThreshold'') IS NULL THEN 2 WHEN (IsSystemDB = 0 AND IsFullRecovery = 1) AND (SELECT [Value] FROM [Inspector].[Settings] WHERE [Description] = ''DiffBackupThreshold'') IS NOT NULL THEN 3 END AS TotalBackupTypes FROM Validations;' END ELSE BEGIN EXEC sp_executesql N'ALTER VIEW [Inspector].[PowerBIBackupsview] AS --Revision Date: 01/05/2019 WITH RawData AS (SELECT Log_Date, LTRIM(RTRIM(BackupSet.Databasename)) AS Databasename, --Added trim as Leading and trailing spaces can cause misreporting [FULL] AS LastFull, [DIFF] AS LastDiff, [LOG] AS LastLog, BackupSet.AGname, CASE WHEN BackupSet.AGname = ''Not in an AG'' THEN BackupSet.Servername ELSE BackupSet.AGname END AS GroupingMethod, BackupSet.Servername, BackupSet.IsFullRecovery, BackupSet.IsSystemDB, BackupSet.primary_replica, BackupSet.backup_preference FROM [Inspector].[BackupsCheck] BackupSet INNER JOIN [Inspector].[CurrentServers] ON BackupSet.Servername = CurrentServers.Servername WHERE CAST(GETDATE() AS DATE) >= CAST(GETDATE() AS DATE) AND CurrentServers.IsActive = 1 AND NOT EXISTS (SELECT 1 FROM [Inspector].[BackupsCheckExcludes] WHERE [Servername] = [BackupSet].[Servername] AND [Databasename] = [BackupSet].[Databasename] AND ([SuppressUntil] IS NULL OR [SuppressUntil] >= GETDATE()) ) ), Aggregates AS ( SELECT MAX(Log_Date) AS Log_Date, RawData.Databasename, MAX(Servername) AS Servername, MAX(LastFull) AS LastFull, MAX(LastDiff) AS LastDiff, MAX(LastLog) AS LastLog, AGname, GroupingMethod, IsFullRecovery, IsSystemDB, MAX(primary_replica) AS primary_replica, UPPER(backup_preference) AS backup_preference FROM RawData RawData GROUP BY Databasename,AGname,GroupingMethod,IsFullRecovery,IsSystemDB,backup_preference ), Validations AS ( SELECT Log_Date, Databasename, Servername, LastFull, LastDiff, LastLog, DATEDIFF(DAY,LastFull,Log_Date) AS FullBackupAge, DATEDIFF(DAY,LastDiff,Log_Date) AS DiffBackupAge, DATEDIFF(MINUTE,LastLog,Log_Date) AS LogBackupAge, AGname, GroupingMethod, IsFullRecovery, IsSystemDB, primary_replica, backup_preference, (SELECT ISNULL(CAST([Value] AS INT),8) FROM [Inspector].[Settings] WHERE [Description] = ''FullBackupThreshold'') AS FullBackupThreshold, (SELECT ISNULL(CAST([Value] AS INT),365) FROM [Inspector].[Settings] WHERE [Description] = ''DiffBackupThreshold'') AS DiffBackupThreshold, (SELECT ISNULL(CAST([Value] AS INT),60) FROM [Inspector].[Settings] WHERE [Description] = ''LogBackupThreshold'') AS LogBackupThreshold FROM Aggregates ) SELECT Log_Date, Databasename, Servername, NULLIF(LastFull,''19000101 00:00:00'') AS LastFull, NULLIF(LastDiff,''19000101 00:00:00'') AS LastDiff, NULLIF(LastLog,''19000101 00:00:00'') AS LastLog, FullBackupAge, DiffBackupAge, LogBackupAge, REPLACE(AGname,''Not in an AG'',''Non AG'') AS AGname, GroupingMethod, IsFullRecovery, IsSystemDB, primary_replica, backup_preference, FullBackupThreshold, DiffBackupThreshold, LogBackupThreshold, CASE WHEN FullBackupAge > FullBackupThreshold THEN 1 ELSE 0 END AS FullBackupBreach, CASE WHEN IsSystemDB = 1 THEN 0 WHEN IsSystemDB = 0 AND (SELECT [Value] FROM [Inspector].[Settings] WHERE [Description] = ''DiffBackupThreshold'') IS NULL THEN 0 WHEN IsSystemDB = 0 AND (SELECT [Value] FROM [Inspector].[Settings] WHERE [Description] = ''DiffBackupThreshold'') IS NOT NULL AND DiffBackupAge > DiffBackupThreshold THEN 1 ELSE 0 END AS DiffBackupBreach, CASE WHEN IsSystemDB = 1 OR IsFullRecovery = 0 THEN 0 WHEN (IsSystemDB = 0 AND IsFullRecovery = 1) AND LogBackupAge > LogBackupThreshold THEN 1 ELSE 0 END AS LogBackupBreach, CASE WHEN IsSystemDB = 1 OR IsFullRecovery = 0 THEN 1 WHEN (IsSystemDB = 0 AND IsFullRecovery = 1) AND (SELECT [Value] FROM [Inspector].[Settings] WHERE [Description] = ''DiffBackupThreshold'') IS NULL THEN 2 WHEN (IsSystemDB = 0 AND IsFullRecovery = 1) AND (SELECT [Value] FROM [Inspector].[Settings] WHERE [Description] = ''DiffBackupThreshold'') IS NOT NULL THEN 3 END AS TotalBackupTypes FROM Validations;' END IF OBJECT_ID('Inspector.DatabaseGrowthInfo') IS NULL BEGIN EXEC sp_executesql N' CREATE VIEW [Inspector].[DatabaseGrowthInfo] AS --Revision date 09/04/2019 SELECT [GrowthInfo].[Servername], [GrowthInfo].[Database_name], [GrowthInfo].[Drive], [GrowthInfo].[FileName], [GrowthInfo].[FirstRecordedGrowth], DATEDIFF(DAY,[GrowthInfo].[FirstRecordedGrowth],CAST(GETDATE() AS DATE)) AS FirstRecordedGrowthAge_Days, [GrowthInfo].[TotalGrowths], [GrowthInfo].[File_id], [DatabaseFileSizes].[GrowthRate] AS GrowthRate_MB, [GrowthInfo].[TotalGrowth_MB], [GrowthInfo].[TotalGrowth_MB]/DATEDIFF(DAY,[GrowthInfo].FirstRecordedGrowth,CAST(GETDATE() AS DATE)) AS AverageDailyGrowth_MB, (([GrowthInfo].[TotalGrowth_MB]/DATEDIFF(DAY,[GrowthInfo].FirstRecordedGrowth,CAST(GETDATE() AS DATE)))*365)/12 AS AverageMonthlyGrowth_MB, ([GrowthInfo].[TotalGrowth_MB]/DATEDIFF(DAY,[GrowthInfo].FirstRecordedGrowth,CAST(GETDATE() AS DATE)))*365 AS AverageYearlyGrowth_MB FROM ( SELECT [Servername], [Database_name], [Drive], [FileName], CAST(MIN([Log_Date]) AS DATE) AS FirstRecordedGrowth, COUNT([Log_Date]) AS TotalGrowths, [File_id], SUM([PostGrowthSize_MB]-[PreGrowthSize_MB]) AS TotalGrowth_MB FROM [Inspector].[DatabaseFileSizeHistory] GROUP BY [Servername], [Database_name], [Drive], [FileName], [File_id] ) GrowthInfo INNER JOIN [Inspector].[DatabaseFileSizes] ON [DatabaseFileSizes].[Database_name] = [GrowthInfo].[Database_name] AND [DatabaseFileSizes].[Servername] = [GrowthInfo].[Servername] AND [DatabaseFileSizes].[File_id] = [GrowthInfo].[File_id];'; END ELSE BEGIN EXEC sp_executesql N' ALTER VIEW [Inspector].[DatabaseGrowthInfo] AS --Revision date 09/04/2019 SELECT [GrowthInfo].[Servername], [GrowthInfo].[Database_name], [GrowthInfo].[Drive], [GrowthInfo].[FileName], [GrowthInfo].[FirstRecordedGrowth], DATEDIFF(DAY,[GrowthInfo].[FirstRecordedGrowth],CAST(GETDATE() AS DATE)) AS FirstRecordedGrowthAge_Days, [GrowthInfo].[TotalGrowths], [GrowthInfo].[File_id], [DatabaseFileSizes].[GrowthRate] AS GrowthRate_MB, [GrowthInfo].[TotalGrowth_MB], [GrowthInfo].[TotalGrowth_MB]/DATEDIFF(DAY,[GrowthInfo].FirstRecordedGrowth,CAST(GETDATE() AS DATE)) AS AverageDailyGrowth_MB, (([GrowthInfo].[TotalGrowth_MB]/DATEDIFF(DAY,[GrowthInfo].FirstRecordedGrowth,CAST(GETDATE() AS DATE)))*365)/12 AS AverageMonthlyGrowth_MB, ([GrowthInfo].[TotalGrowth_MB]/DATEDIFF(DAY,[GrowthInfo].FirstRecordedGrowth,CAST(GETDATE() AS DATE)))*365 AS AverageYearlyGrowth_MB FROM ( SELECT [Servername], [Database_name], [Drive], [FileName], CAST(MIN([Log_Date]) AS DATE) AS FirstRecordedGrowth, COUNT([Log_Date]) AS TotalGrowths, [File_id], SUM([PostGrowthSize_MB]-[PreGrowthSize_MB]) AS TotalGrowth_MB FROM [Inspector].[DatabaseFileSizeHistory] GROUP BY [Servername], [Database_name], [Drive], [FileName], [File_id] ) GrowthInfo INNER JOIN [Inspector].[DatabaseFileSizes] ON [DatabaseFileSizes].[Database_name] = [GrowthInfo].[Database_name] AND [DatabaseFileSizes].[Servername] = [GrowthInfo].[Servername] AND [DatabaseFileSizes].[File_id] = [GrowthInfo].[File_id];'; END IF OBJECT_ID('Inspector.DriveSpaceInfo') IS NULL BEGIN EXEC sp_executesql N'CREATE VIEW [Inspector].[DriveSpaceInfo] AS /* Author: Adrian Buckman Created: 23/08/2018 Revised: 09/07/2021 Description: Show aggregated space used by drive by server, show Average daily,monthly and yearly usage and MIN/MAX Daily Increment variances. */ SELECT Servername, Drive, (SELECT TOP (1) Capacity_GB FROM [Inspector].[DriveSpace] LastCapacity WHERE LastCapacity.Servername = DriveInfo.Servername AND LastCapacity.Drive = DriveInfo.Drive ) AS Capacity_GB, COUNT(*) AS DaysRecorded, CAST(AVG(Delta_GB) AS DECIMAL(8,2)) AS AVG_Daily_Growth_GB, CAST(((AVG(Delta_GB)*365)/12) AS DECIMAL(8,2)) AS AVG_Monthly_Growth_GB, CAST((AVG(Delta_GB)*365) AS DECIMAL(8,2)) AS AVG_Yearly_GB, CAST((MIN(Delta_GB)) AS DECIMAL(8,2)) AS MIN_Daily_Increment_GB, CAST((MAX(Delta_GB)) AS DECIMAL(8,2)) AS MAX_Daily_Increment_GB FROM (SELECT Servername, Log_Date, Drive, Capacity_GB, AvailableSpace_GB, Used_GB, CASE WHEN Delta_GB < 0 THEN 0 ELSE Delta_GB END AS Delta_GB FROM ( SELECT Servername, Log_Date, Drive, Capacity_GB, AvailableSpace_GB, Capacity_GB - AvailableSpace_GB AS Used_GB, --LAG(Capacity_GB - AvailableSpace_GB,1,NULL) OVER (PARTITION BY Servername,Drive ORDER BY Servername ASC, Drive ASC, Log_Date ASC) Prev_used_GB, Capacity_GB - AvailableSpace_GB - LAG(Capacity_GB - AvailableSpace_GB,1,Capacity_GB - AvailableSpace_GB) OVER (PARTITION BY Servername,Drive ORDER BY Servername ASC, Drive ASC, Log_Date ASC) AS Delta_GB FROM [Inspector].[DriveSpace] ) AS Derived ) AS DriveInfo GROUP BY Servername, Drive '; END ELSE BEGIN EXEC sp_executesql N'ALTER VIEW [Inspector].[DriveSpaceInfo] AS /* Author: Adrian Buckman Created: 23/08/2018 Revised: 09/07/2021 Description: Show aggregated space used by drive by server, show Average daily,monthly and yearly usage and MIN/MAX Daily Increment variances. */ SELECT Servername, Drive, (SELECT TOP (1) Capacity_GB FROM [Inspector].[DriveSpace] LastCapacity WHERE LastCapacity.Servername = DriveInfo.Servername AND LastCapacity.Drive = DriveInfo.Drive ) AS Capacity_GB, COUNT(*) AS DaysRecorded, CAST(AVG(Delta_GB) AS DECIMAL(8,2)) AS AVG_Daily_Growth_GB, CAST(((AVG(Delta_GB)*365)/12) AS DECIMAL(8,2)) AS AVG_Monthly_Growth_GB, CAST((AVG(Delta_GB)*365) AS DECIMAL(8,2)) AS AVG_Yearly_GB, CAST((MIN(Delta_GB)) AS DECIMAL(8,2)) AS MIN_Daily_Increment_GB, CAST((MAX(Delta_GB)) AS DECIMAL(8,2)) AS MAX_Daily_Increment_GB FROM (SELECT Servername, Log_Date, Drive, Capacity_GB, AvailableSpace_GB, Used_GB, CASE WHEN Delta_GB < 0 THEN 0 ELSE Delta_GB END AS Delta_GB FROM ( SELECT Servername, Log_Date, Drive, Capacity_GB, AvailableSpace_GB, Capacity_GB - AvailableSpace_GB AS Used_GB, --LAG(Capacity_GB - AvailableSpace_GB,1,NULL) OVER (PARTITION BY Servername,Drive ORDER BY Servername ASC, Drive ASC, Log_Date ASC) Prev_used_GB, Capacity_GB - AvailableSpace_GB - LAG(Capacity_GB - AvailableSpace_GB,1,Capacity_GB - AvailableSpace_GB) OVER (PARTITION BY Servername,Drive ORDER BY Servername ASC, Drive ASC, Log_Date ASC) AS Delta_GB FROM [Inspector].[DriveSpace] ) AS Derived ) AS DriveInfo GROUP BY Servername, Drive '; END --Create Powershell collection objects IF OBJECT_ID('Inspector.PSEnabledModules') IS NULL BEGIN EXEC ('CREATE VIEW [Inspector].[PSEnabledModules] AS --Revision date: 27/01/2020 SELECT [ModuleConfig_Desc], [Modulename], [CollectionProcedurename], IsActive AS [Enabled] FROM [Inspector].[Modules] WHERE IsActive = 1;'); END ELSE BEGIN EXEC ('ALTER VIEW [Inspector].[PSEnabledModules] AS --Revision date: 27/01/2020 SELECT [ModuleConfig_Desc], [Modulename], [CollectionProcedurename], IsActive AS [Enabled] FROM [Inspector].[Modules] WHERE IsActive = 1;'); END IF OBJECT_ID('Inspector.PSInspectorTables') IS NULL BEGIN EXEC sp_executesql N' CREATE VIEW [Inspector].[PSInspectorTables] AS --Revision date: 14/10/2019 SELECT DISTINCT Modulename AS Tablename FROM [Inspector].[Modules] INNER JOIN sys.tables on Modules.Modulename = tables.name WHERE tables.schema_id = SCHEMA_ID(N''Inspector'');' END ELSE BEGIN EXEC sp_executesql N' ALTER VIEW [Inspector].[PSInspectorTables] AS --Revision date: 14/10/2019 SELECT DISTINCT Modulename AS Tablename FROM [Inspector].[Modules] INNER JOIN sys.tables on Modules.Modulename = tables.name WHERE tables.schema_id = SCHEMA_ID(N''Inspector'');' END IF OBJECT_ID('Inspector.PSADHocDatabaseSupressionStage') IS NULL CREATE TABLE [Inspector].[PSADHocDatabaseSupressionStage]( [Servername] [nvarchar](128) NULL, [Log_Date] [datetime] NULL, [Databasename] [nvarchar](128) NULL, [Suppress] [bit] NULL ); IF OBJECT_ID('Inspector.PSAutoUpdate') IS NULL CREATE TABLE [Inspector].[PSAutoUpdate]( [Updatename] [nvarchar](128) NOT NULL, [LastUpdated] [datetime] NULL ); IF NOT EXISTS(SELECT 1 FROM [Inspector].[PSAutoUpdate] WHERE [Updatename] = 'PSAutoUpdate') BEGIN INSERT INTO [Inspector].[PSAutoUpdate] ([Updatename],[LastUpdated]) VALUES('PSAutoUpdate',NULL); END IF OBJECT_ID('Inspector.PSAGDatabasesStage') IS NULL CREATE TABLE [Inspector].[PSAGDatabasesStage]( [ID] [int] IDENTITY(1,1) NOT NULL, [Servername] [nvarchar](128) NULL, [Log_Date] [datetime] NULL, [LastUpdated] [datetime] NULL, [Databasename] [nvarchar](128) NULL, [Is_AG] [bit] NULL, [Is_AGJoined] [bit] NULL ); IF OBJECT_ID('Inspector.PSDatabaseFileSizesStage') IS NULL BEGIN CREATE TABLE [Inspector].[PSDatabaseFileSizesStage]( [Servername] [nvarchar](128) NOT NULL, [Database_id] [int] NOT NULL, [Database_name] [nvarchar](128) NULL, [OriginalDateLogged] [datetime] NOT NULL, [OriginalSize_MB] [bigint] NULL, [Type_desc] [nvarchar](60) NULL, [File_id] [tinyint] NOT NULL, [Filename] [nvarchar](260) NULL, [PostGrowthSize_MB] [bigint] NULL, [GrowthRate] [int] NULL, [Is_percent_growth] [bit] NOT NULL, [NextGrowth] [bigint] NULL, [LastUpdated] [datetime] NULL ); END IF OBJECT_ID('Inspector.PSDatabaseFileSizeHistoryStage') IS NULL BEGIN CREATE TABLE [Inspector].[PSDatabaseFileSizeHistoryStage]( [GrowthID] [bigint] NOT NULL, [Servername] [nvarchar](128) NOT NULL, [Database_id] [int] NOT NULL, [Database_name] [nvarchar](128) NOT NULL, [Log_Date] [datetime] NOT NULL, [Type_Desc] [nvarchar](60) NOT NULL, [File_id] [tinyint] NOT NULL, [FileName] [nvarchar](260) NOT NULL, [PreGrowthSize_MB] [bigint] NOT NULL, [GrowthRate_MB] [int] NOT NULL, [GrowthIncrements] [int] NOT NULL, [PostGrowthSize_MB] [bigint] NOT NULL, [Drive] [NVARCHAR](128), ); END IF NOT EXISTS(SELECT 1 FROM sys.columns WHERE name = N'Drive' AND [object_id] = OBJECT_ID(N'Inspector.PSDatabaseFileSizeHistoryStage')) BEGIN --New column for 1.4 ALTER TABLE [Inspector].[PSDatabaseFileSizeHistoryStage] ADD [Drive] NVARCHAR(128); END --Increase column length to accomodate shared storage names such as \\ClusterStorage IF EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.PSDatabaseFileSizeHistoryStage') AND [name] = 'Drive' AND max_length != 256) BEGIN ALTER TABLE [Inspector].[PSDatabaseFileSizeHistoryStage] ALTER COLUMN [Drive] NVARCHAR(128); END IF OBJECT_ID('Inspector.PSADHocDatabaseCreationsStage') IS NULL CREATE TABLE [Inspector].[PSADHocDatabaseCreationsStage]( [Servername] [nvarchar](128) NOT NULL, [Log_Date] [datetime] NULL, [Databasename] [nvarchar](128) NOT NULL, [Create_Date] [datetime] NULL ); IF OBJECT_ID('Inspector.PSDriveSpaceStage') IS NULL CREATE TABLE [Inspector].[PSDriveSpaceStage]( [Servername] [nvarchar](128) NULL, [Log_Date] [datetime] NULL, [Drive] [nvarchar](128) NULL, [Capacity_GB] [decimal](10, 2) NULL, [AvailableSpace_GB] [decimal](10, 2) NULL ); --Increase column length to accomodate shared storage names such as \\ClusterStorage IF EXISTS (SELECT 1 FROM sys.columns WHERE object_id = OBJECT_ID('Inspector.PSDriveSpaceStage') AND [name] = 'Drive' AND max_length != 256) BEGIN ALTER TABLE [Inspector].[PSDriveSpaceStage] ALTER COLUMN [Drive] NVARCHAR(128); END IF OBJECT_ID('Inspector.PSInstanceVersionHistoryStage') IS NULL CREATE TABLE [Inspector].[PSInstanceVersionHistoryStage]( [Servername] NVARCHAR(128) NOT NULL, [Log_Date] DATETIME NOT NULL, [CollectionDatetime] DATETIME NOT NULL, [VersionNo] NVARCHAR(128) NULL, [Edition] NVARCHAR(128) NULL ); IF EXISTS (SELECT 1 FROM sys.all_columns WHERE object_id = OBJECT_ID('Inspector.PSInstanceVersionHistoryStage') AND name = 'VersionNo' AND max_length < 256) BEGIN ALTER TABLE [Inspector].[PSInstanceVersionHistoryStage] ALTER COLUMN [VersionNo] NVARCHAR(128); END IF OBJECT_ID('Inspector.PSAGPrimaryHistoryStage') IS NULL BEGIN CREATE TABLE [Inspector].[PSAGPrimaryHistoryStage]( [Log_Date] DATETIME NULL, [CollectionDateTime] DATETIME NULL, [Servername] NVARCHAR(128) NULL, [AGname] NVARCHAR(128) NULL ); END --Typo Fix 1.0.1 IF OBJECT_ID('Inspector.LoginAttemptsiInsert') IS NOT NULL DROP PROCEDURE [Inspector].[LoginAttemptsiInsert]; --Drop functions for recreation IF OBJECT_ID('Inspector.GetNonServerSpecificModules') IS NOT NULL DROP FUNCTION [Inspector].[GetNonServerSpecificModules]; IF OBJECT_ID('Inspector.GenerateHtmlTableheader') IS NOT NULL DROP FUNCTION [Inspector].[GenerateHtmlTableheader]; IF OBJECT_ID('Inspector.GetWarningLevel') IS NOT NULL DROP FUNCTION [Inspector].[GetWarningLevel]; IF OBJECT_ID('Inspector.GetDebugFlag') IS NOT NULL DROP FUNCTION [Inspector].[GetDebugFlag]; IF OBJECT_ID('Inspector.GetServerInfo') IS NOT NULL DROP FUNCTION [Inspector].[GetServerInfo]; --Drop Trigger for recreation IF OBJECT_ID('Inspector.PSConfigSync') IS NOT NULL DROP TRIGGER [Inspector].[PSConfigSync]; SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' -- ============================================= -- This trigger is used to keep the table PSConfig up to date with the Modules table -- ============================================= CREATE TRIGGER [Inspector].[PSConfigSync] ON [Inspector].[Modules] AFTER DELETE,UPDATE AS BEGIN SET NOCOUNT ON; --Revision date: 23/10/2019 --Inserts into the Modules table are not handled by this trigger , the Procedure [Inspector].[PopulatePSConfig] is responsible --for that as there is additional logic that needs to be applied for Inspector default modules. DECLARE @DriveSpaceRetentionPeriodInDays VARCHAR(6); SET @DriveSpaceRetentionPeriodInDays = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@@SERVERNAME, ''DriveSpace'', ''DriveSpaceRetentionPeriodInDays'') AS INT), 90)); --Update IsActive flag UPDATE PS SET IsActive = inserted.IsActive FROM [Inspector].[PSConfig] PS INNER JOIN inserted ON PS.ModuleConfig_Desc = inserted.ModuleConfig_Desc AND PS.Modulename = inserted.Modulename WHERE inserted.IsActive != PS.IsActive; --Add rows when IsActive is set to 1 and rows do not already exist in PSConfig INSERT INTO [Inspector].[PSConfig] ([Servername], [ModuleConfig_Desc], [Modulename], [Procedurename], [Tablename], [StageTablename], [StageProcname], [TableAction], [InsertAction], [RetentionInDays],[IsActive]) SELECT [ActiveServers]. [Servername], inserted.[ModuleConfig_Desc], [Modulename], [CollectionProcedurename], CASE WHEN [inserted].[Modulename] = ''DatabaseGrowths'' THEN ''DatabaseFileSizes,DatabaseFileSizeHistory'' WHEN [inserted].[Modulename] = ''ADHocDatabaseCreations'' THEN ''ADHocDatabaseCreations,ADHocDatabaseSupression'' WHEN [inserted].[Modulename] = ''AGCheck'' THEN ''AGCheck,AGPrimaryHistory'' ELSE REPLACE([CollectionProcedurename],''Insert'','''') END AS Tablename, CASE WHEN [inserted].[Modulename] IN (''AGDatabases'',''DriveSpace'') THEN ''PS''+[inserted].[Modulename]+''Stage'' WHEN [inserted].[Modulename] = ''ADHocDatabaseCreations'' THEN ''PSADHocDatabaseCreationsStage,PSADHocDatabaseSupressionStage'' WHEN [inserted].[Modulename] = ''DatabaseGrowths'' THEN ''PSDatabaseFileSizesStage,PSDatabaseFileSizeHistoryStage'' WHEN [inserted].[Modulename] = ''AGCheck'' THEN ''N/A,PSAGPrimaryHistoryStage'' ELSE NULL END AS StageTablename, CASE WHEN [inserted].[Modulename] IN (''AGDatabases'', ''DriveSpace'', ''DatabaseGrowths'', ''ADHocDatabaseCreations'') THEN ''PSGet''+[inserted].[Modulename]+''Stage'' WHEN [inserted].[Modulename] = ''AGCheck'' THEN ''N/A,PSGetAGPrimaryHistoryStage'' ELSE NULL END AS StageProcname, CASE WHEN [inserted].[Modulename] IN (''AGDatabases'',''DriveSpace'') THEN ''3'' WHEN [inserted].[Modulename] IN (''ADHocDatabaseCreations'',''DatabaseGrowths'') THEN ''3,3'' WHEN [inserted].[Modulename] = ''AGCheck'' THEN ''1,3'' ELSE ''1'' END AS TableAction, --1 delete, 2 delete with retention, 3 Stage/merge CASE WHEN [inserted].[Modulename] IN (''AGDatabases'',''BackupSizesByDay'') THEN ''1'' WHEN [inserted].[Modulename] IN (''ADHocDatabaseCreations'',''AGCheck'') THEN ''1,1'' WHEN [inserted].[Modulename] = ''DatabaseGrowths'' THEN ''1,2'' ELSE ''2'' END AS InsertAction, --1 ALL, 2 Todays'''' data only CASE WHEN [inserted].[Modulename] = (''DatabaseGrowths'') THEN @DriveSpaceRetentionPeriodInDays+'',''+@DriveSpaceRetentionPeriodInDays WHEN [inserted].[Modulename] = (''DriveSpace'') THEN @DriveSpaceRetentionPeriodInDays ELSE NULL END AS RetentionInDays, 1 FROM inserted INNER JOIN ( SELECT [Servername], ISNULL([ModuleConfig_Desc], ''Default'') AS [ModuleConfig_Desc] FROM [Inspector].[CurrentServers] WHERE IsActive = 1 ) AS ActiveServers ON inserted.[ModuleConfig_Desc] = ActiveServers.[ModuleConfig_Desc] WHERE inserted.IsActive = 1 AND NOT EXISTS (SELECT 1 FROM [Inspector].[PSConfig] PS WHERE PS.ModuleConfig_Desc = inserted.ModuleConfig_Desc AND PS.Modulename = inserted.Modulename ); --Remove rows that have been deleted from Modules DELETE PS FROM [Inspector].[PSConfig] PS INNER JOIN deleted ON PS.ModuleConfig_Desc = deleted.ModuleConfig_Desc AND PS.Modulename = deleted.Modulename WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[Modules] DeletedModule WHERE DeletedModule.ModuleConfig_Desc = deleted.ModuleConfig_Desc AND DeletedModule.Modulename = deleted.Modulename ); END' EXEC(@SQLStatement); IF OBJECT_ID('Inspector.GetMonitorHours') IS NULL BEGIN EXEC sp_executesql N'CREATE PROCEDURE [Inspector].[GetMonitorHours] AS;'; END EXEC sp_executesql N'ALTER PROCEDURE [Inspector].[GetMonitorHours] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @MonitorHourStart INT OUTPUT, @MonitorHourEnd INT OUTPUT ) AS BEGIN /* Revision date: 01/05/2020 */ SELECT @MonitorHourStart = [MonitorHourStart], @MonitorHourEnd = [MonitorHourEnd] FROM [Inspector].[MonitorHours] WHERE [Servername] = @Servername AND [Modulename] = @Modulename; END'; IF OBJECT_ID('Inspector.GetModuleConfigFrequency') IS NULL BEGIN EXEC sp_executesql N'CREATE PROCEDURE [Inspector].[GetModuleConfigFrequency] AS;'; END EXEC sp_executesql N'ALTER PROCEDURE [Inspector].[GetModuleConfigFrequency] ( @ModuleConfig VARCHAR(20), @Frequency INT OUTPUT ) AS BEGIN /* Revision date: 01/05/2020 */ SELECT @Frequency = [Frequency] FROM [Inspector].[ModuleConfig] WHERE ModuleConfig_Desc = @ModuleConfig; END'; IF OBJECT_ID('Inspector.ADHocDatabaseCreationsInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[ADHocDatabaseCreationsInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[ADHocDatabaseCreationsInsert] AS BEGIN --Revision date: 28/06/2018 DECLARE @Servername NVARCHAR(128) = @@SERVERNAME; DELETE FROM [Inspector].[ADHocDatabaseCreations] WHERE Servername = @Servername; INSERT INTO [Inspector].[ADHocDatabaseCreations] (Servername,Log_Date,Databasename,Create_Date) SELECT @Servername, GETDATE(), name, Create_date FROM sys.databases WHERE (name LIKE ''[A-Z]%[0-9]%'' OR name LIKE ''%Restored%'' OR name LIKE ''%Copy%'') AND [state] = 0 AND [create_date] > DATEADD(DAY,-7,CAST(GETDATE() AS DATE)) AND [source_database_id] IS NULL AND name COLLATE DATABASE_DEFAULT NOT IN (SELECT Databasename FROM [Inspector].[ADHocDatabaseSupression] WHERE Servername = @Servername AND Suppress = 1) ORDER BY create_date ASC; INSERT INTO [Inspector].[ADHocDatabaseSupression] (Servername, Log_Date, Databasename, Suppress) SELECT @Servername, GETDATE(), Databasename, 0 FROM [Inspector].[ADHocDatabaseCreations] Creations WHERE Servername = @Servername AND NOT EXISTS (SELECT Databasename FROM [Inspector].[ADHocDatabaseSupression] SuppressList WHERE SuppressList.Servername = @Servername AND SuppressList.Databasename = Creations.Databasename); IF NOT EXISTS (SELECT Servername FROM [Inspector].[ADHocDatabaseCreations] WHERE Servername = @Servername) BEGIN INSERT INTO [Inspector].[ADHocDatabaseCreations] (Servername,Log_Date,Databasename,Create_Date) VALUES(@Servername,GETDATE(),''No Ad hoc database creations present'',NULL) END END;'; IF OBJECT_ID('Inspector.AGCheckInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[AGCheckInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[AGCheckInsert] AS BEGIN --Revision date: 24/04/2019 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DECLARE @PrimaryHistoryRetention INT = (SELECT ISNULL(NULLIF([Value],''''),90) From [Inspector].[Settings] Where Description = ''AGPrimaryHistoryRetentionPeriodInDays''); INSERT INTO [Inspector].[AGPrimaryHistory] ([Log_Date], [CollectionDateTime], [Servername], [AGname]) --Is this server now a primary since the last Inspector collection SELECT [Log_Date], GETDATE(), @Servername, [AGname] FROM sys.dm_hadr_availability_group_states States INNER JOIN sys.availability_groups Groups ON States.group_id = Groups.group_id INNER JOIN (SELECT [Log_Date],[AGname] FROM [Inspector].[AGCheck] WHERE [ReplicaServername] = @Servername AND [ReplicaRole] = N''SECONDARY'' ) AS SecondaryCheck ON [Groups].[name] = [SecondaryCheck].[AGname] COLLATE DATABASE_DEFAULT WHERE States.primary_replica = @Servername AND NOT EXISTS (SELECT 1 FROM [Inspector].[AGPrimaryHistory] WHERE [AGPrimaryHistory].[Log_Date] = [SecondaryCheck].[Log_Date] AND [AGPrimaryHistory].[AGname] = [SecondaryCheck].[AGname] AND [AGPrimaryHistory].[Servername] = @Servername) DELETE FROM [Inspector].[AGCheck] WHERE Servername = @Servername; DELETE FROM [Inspector].[AGPrimaryHistory] WHERE Servername = @Servername AND [Log_Date] <= DATEADD(DAY,-@PrimaryHistoryRetention,GETDATE()); IF SERVERPROPERTY(''IsHadrEnabled'') = 1 AND EXISTS (SELECT name FROM sys.availability_groups) BEGIN INSERT INTO [Inspector].[AGCheck] ([Servername], [Log_Date], [AGname], [State], [ReplicaServername], [Suspended], [SuspendReason], [FailoverReady], [ReplicaRole]) SELECT DISTINCT @Servername, GETDATE(), Groups.name AS AGNAME, States.synchronization_health_desc, Replicas.replica_server_name, ReplicaStates.is_suspended, ISNULL(ReplicaStates.suspend_reason_desc,''N/A'') AS suspend_reason_desc, FailoverReady.is_failover_ready, States.role_desc FROM sys.availability_groups Groups INNER JOIN sys.dm_hadr_availability_replica_states as States ON States.group_id = Groups.group_id INNER JOIN sys.availability_replicas as Replicas ON States.replica_id = Replicas.replica_id INNER JOIN sys.dm_hadr_database_replica_cluster_states FailoverReady ON Replicas.replica_id = FailoverReady.replica_id INNER JOIN sys.dm_hadr_database_replica_states as ReplicaStates ON Replicas.replica_id = ReplicaStates.replica_id; --Update AG Replica count if it has changed UPDATE [AGCheckConfig] SET [AGReplicaCount] = [ReplicaCount] FROM ( SELECT Groups.[name], COUNT([name]) AS ReplicaCount FROM sys.availability_groups Groups INNER JOIN sys.availability_replicas as Replicas ON Groups.group_id = Replicas.group_id GROUP BY Groups.[name] ) AS ReplicaCounts INNER JOIN [Inspector].[AGCheckConfig] ON [AGname] = [ReplicaCounts].[name] COLLATE DATABASE_DEFAULT WHERE [AGCheckConfig].[AGReplicaCount] != ReplicaCount; --Insert AG Replica counts and base Failover ready node count config count INSERT INTO [Inspector].[AGCheckConfig] ([AGname],[AGReplicaCount],[FailoverReadyNodeCount]) SELECT Groups.[name], COUNT([name]), 2 FROM sys.availability_groups Groups INNER JOIN sys.availability_replicas as Replicas ON Groups.group_id = Replicas.group_id WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[AGCheckConfig] WHERE [AGname] = Groups.[name] COLLATE DATABASE_DEFAULT) GROUP BY Groups.[name]; END ELSE BEGIN INSERT INTO [Inspector].[AGCheck] ([Servername], [Log_Date], [AGname], [State]) SELECT @Servername, GETDATE(), ''HADR IS NOT ENABLED ON THIS SERVER OR YOU HAVE NO AVAILABILITY GROUPS'', ''N/A'' END END;'; IF OBJECT_ID('Inspector.DatabaseFilesInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatabaseFilesInsert] AS;'); SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' ALTER PROCEDURE [Inspector].[DatabaseFilesInsert] AS BEGIN --Revision date: 06/04/2019 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DECLARE @DataDrives VARCHAR(255) = (SELECT NULLIF([Value],'''') FROM [Inspector].[Settings] WHERE [Description] = ''DataDrives''); DECLARE @LogDrives VARCHAR(255) = (SELECT NULLIF([Value],'''') FROM [Inspector].[Settings] WHERE [Description] = ''LogDrives''); IF OBJECT_ID(''tempdb.dbo.#SplitDrives'') IS NOT NULL DROP TABLE #SplitDrives; CREATE TABLE #SplitDrives ( DriveType CHAR(4), DriveLabel NVARCHAR(20) ); DELETE FROM [Inspector].[DatabaseFiles] WHERE Servername = @Servername; INSERT INTO #SplitDrives (DriveType,DriveLabel) '+CASE WHEN @Compatibility = 0 THEN 'SELECT ''Data'',[StringElement] FROM master.dbo.fn_SplitString(@DataDrives,'','')' ELSE 'SELECT ''Data'',[value] FROM STRING_SPLIT(@DataDrives,'','')' END +' UNION ALL '+CASE WHEN @Compatibility = 0 THEN 'SELECT ''Log'',[StringElement] FROM master.dbo.fn_SplitString(@LogDrives,'','')' ELSE 'SELECT ''Log'',[value] FROM STRING_SPLIT(@LogDrives,'','')' END +'; --Remove any duplicate drive letters i.e C:\ labelled as both Data and Log DELETE FROM #SplitDrives WHERE DriveLabel IN ( SELECT DriveLabel FROM #SplitDrives DataDrives WHERE DriveType = ''Data'' INTERSECT SELECT DriveLabel FROM #SplitDrives LogDrives WHERE DriveType = ''Log'' ); INSERT INTO [Inspector].[DatabaseFiles] (Servername,Log_Date,Databasename,FileType,FilePath) SELECT @Servername, GETDATE(), DB_NAME(database_id), type_desc, physical_name FROM sys.master_files INNER JOIN (SELECT DriveLabel FROM #SplitDrives WHERE DriveType = ''Data'') AS DataDrives ON physical_name LIKE DriveLabel+''%'' WHERE physical_name LIKE ''%.ldf'' UNION ALL SELECT @Servername, GETDATE(), DB_NAME(database_id), type_desc, physical_name FROM sys.master_files INNER JOIN (SELECT DriveLabel FROM #SplitDrives WHERE DriveType = ''Log'') AS DataDrives ON physical_name LIKE DriveLabel+''%'' WHERE physical_name LIKE ''%.mdf''; IF NOT EXISTS (SELECT Servername FROM [Inspector].[DatabaseFiles] WHERE Servername = @Servername) BEGIN INSERT INTO [Inspector].[DatabaseFiles] (Servername,Log_Date,Databasename,FileType,FilePath) VALUES(@Servername,GETDATE(),''No Database File issues present'',NULL,NULL) END END;' EXEC(@SQLStatement); IF OBJECT_ID('Inspector.DatabaseStatesInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatabaseStatesInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DatabaseStatesInsert] AS BEGIN --Revision date: 28/06/2018 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DELETE FROM [Inspector].[DatabaseStates] WHERE Servername = @Servername; INSERT INTO [Inspector].[DatabaseStates] (Servername,Log_Date,DatabaseState,Total,DatabaseNames) SELECT @Servername, GETDATE(), state_desc, COUNT(state_desc), CASE WHEN state_desc IN (''ONLINE'',''SNAPSHOT (less than 10 days old)'') THEN ''-'' ELSE DBName END FROM ( SELECT CASE WHEN source_database_id IS NOT NULL AND create_date < DATEADD(DAY,-10,GETDATE()) THEN ''SNAPSHOT (more than 10 days old)'' WHEN source_database_id IS NOT NULL AND create_date > DATEADD(DAY,-10,GETDATE()) THEN ''SNAPSHOT (less than 10 days old)'' WHEN EXISTS (SELECT 1 FROM msdb.dbo.log_shipping_secondary_databases Logshipped WHERE Logshipped.secondary_database = Databases.name) AND Databases.state = 1 THEN ''LOG SHIPPED RESTORING'' WHEN EXISTS (SELECT 1 FROM msdb.dbo.log_shipping_secondary_databases Logshipped WHERE Logshipped.secondary_database = Databases.name) AND Databases.is_in_standby = 1 THEN ''LOG SHIPPED STANDBY'' ELSE state_desc END AS state_desc, STUFF(COALESCE(LogShipped.Databasename,NonOnlineDBs.Databasename,OldSnapshotDBs.Databasename,''''),1,2,'''') As DBName FROM sys.databases Databases CROSS APPLY (SELECT '' , '' + QUOTENAME(name) FROM sys.databases NonOnlineDBs WHERE Databases.state_desc = NonOnlineDBs.state_desc AND (NonOnlineDBs.state_desc != ''Online'' AND source_database_id IS NULL) AND NOT EXISTS (SELECT 1 FROM msdb.dbo.log_shipping_secondary_databases Logshipped WHERE Logshipped.secondary_database = NonOnlineDBs.name) FOR XML PATH('''')) NonOnlineDBs (Databasename) CROSS APPLY (SELECT '' , '' + QUOTENAME(name) FROM sys.databases OldSnapshotDBs WHERE Databases.state_desc = OldSnapshotDBs.state_desc AND source_database_id IS NOT NULL AND create_date < DATEADD(DAY,-10,GETDATE()) FOR XML PATH('''')) OldSnapshotDBs (Databasename) CROSS APPLY (SELECT '' , '' + QUOTENAME(secondary_database) FROM msdb.dbo.log_shipping_secondary_databases Logshipped INNER JOIN sys.databases DatabasesLS ON Logshipped.secondary_database = DatabasesLS.name WHERE Databases.state_desc = DatabasesLS.state_desc FOR XML PATH('''')) LogShipped (Databasename) ) DatabaseStates GROUP BY state_desc,DBName ORDER BY COUNT(state_desc) DESC END;'; IF OBJECT_ID('Inspector.DriveSpaceInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DriveSpaceInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DriveSpaceInsert] AS BEGIN /************************** Revision date: 01/05/2021 DistinctDrives derived table updated to show all database_id and file_id combinations grouped by file path. Row number is applied so that we can filter just one database_id and file_id combination per file path and then these combinations are passed to the sys.dm_os_volume_stats system TVF , the reason for the filtering within the derived table is to reduce the number of executions performed by the TVF because on instances with lots of databases this can slow execution. **************************/ DECLARE @Retention INT = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@@SERVERNAME, ''DriveSpace'', ''DriveSpaceRetentionPeriodInDays'') AS INT), 90)); DELETE FROM [Inspector].[DriveSpace] WHERE Log_Date < DATEADD(DAY,-@Retention,GETDATE()) AND Servername = @@SERVERNAME; INSERT INTO [Inspector].[DriveSpace] (Servername, Log_Date, Drive, Capacity_GB, AvailableSpace_GB, UsedSpaceGB,PrevUsedSpace_GB) SELECT LatestDriveSpace.Servername, LatestDriveSpace.Log_Date, LatestDriveSpace.Drive, LatestDriveSpace.Capacity_GB, LatestDriveSpace.AvailableSpace_GB, CAST(LatestDriveSpace.[Capacity_GB]-LatestDriveSpace.[AvailableSpace_GB] AS DECIMAL(10,2)), ISNULL(LastRecordedSpace.PrevUsedSpace_GB,CAST(LatestDriveSpace.[Capacity_GB]-LatestDriveSpace.[AvailableSpace_GB] AS DECIMAL(10,2))) AS PrevUsedSpace_GB FROM ( SELECT DISTINCT @@SERVERNAME AS Servername, GETDATE() AS Log_Date, CAST(UPPER(volumestats.volume_mount_point) AS NVARCHAR(128)) AS Drive, CAST((CAST(volumestats.total_bytes AS DECIMAL(20,2)))/1024/1024/1024 AS DECIMAL(10,2)) Capacity_GB, CAST((CAST(volumestats.available_bytes AS DECIMAL(20,2)))/1024/1024/1024 AS DECIMAL(10,2)) AS AvailableSpace_GB FROM ( SELECT [database_id], [file_id], ROW_NUMBER() OVER (PARTITION BY SUBSTRING(physical_name,1,LEN(physical_name)-CHARINDEX(''\'',REVERSE(physical_name))+1) ORDER BY SUBSTRING(physical_name,1,LEN(physical_name)-CHARINDEX(''\'',REVERSE(physical_name))+1) ASC) AS RowNum FROM sys.master_files WHERE database_id IN (SELECT database_id FROM sys.databases WHERE state = 0) ) DistinctDrives CROSS APPLY sys.dm_os_volume_stats([DistinctDrives].[database_id],[DistinctDrives].[file_id]) volumestats WHERE DistinctDrives.RowNum = 1 ) AS LatestDriveSpace OUTER APPLY (SELECT TOP (1) [UsedSpaceGB] AS PrevUsedSpace_GB FROM [Inspector].[DriveSpace] WHERE LatestDriveSpace.Servername = DriveSpace.Servername AND LatestDriveSpace.[Drive] = DriveSpace.[Drive] ORDER BY [Log_Date] DESC) LastRecordedSpace; END'; IF OBJECT_ID('Inspector.PopulateModuleWarningLevel') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PopulateModuleWarningLevel] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PopulateModuleWarningLevel] AS BEGIN --Revision date: 16/03/2019 /* These modules are excluded from warning level control: BackupsCheck DriveSpace TopFiveDatabases DatabaseGrowthCheck BackupSizesCheck */ INSERT INTO [Inspector].[ModuleWarningLevel] ([ModuleConfig_Desc],[Module]) SELECT [ModuleConfig_Desc],[Modulename] FROM [Inspector].[Modules] [ModulesList] WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[ModuleWarningLevel] WHERE [ModulesList].[ModuleConfig_Desc] = [ModuleWarningLevel].[ModuleConfig_Desc] AND [ModulesList].[Modulename] = [ModuleWarningLevel].[Module] ) UNION SELECT [Modules].[ModuleConfig_Desc], [CatalogueModulesList].[Module] FROM [Inspector].[Modules] CROSS JOIN (SELECT [Module] FROM (VALUES(''CatalogueMissingLogins''),(''CatalogueDroppedTables''),(''CatalogueDroppedDatabases'')) ModuleList(Module)) AS CatalogueModulesList WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[ModuleWarningLevel] WHERE [ModuleConfig_Desc] = [Modules].[ModuleConfig_Desc] AND [Module] = [CatalogueModulesList].[Module]); END'; --Run Proc for the first time to populate the Warning Level table EXEC sp_executesql N'EXEC [Inspector].[PopulateModuleWarningLevel];'; IF OBJECT_ID('Inspector.FailedAgentJobsInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[FailedAgentJobsInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[FailedAgentJobsInsert] AS BEGIN --Revision date: 28/06/2018 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DELETE FROM [Inspector].[FailedAgentJobs] WHERE Servername = @Servername; INSERT INTO [Inspector].[FailedAgentJobs] (Servername,Log_Date,Jobname,LastStepFailed,LastFailedDate,LastError) SELECT @Servername, GETDATE(), Jobs.name, JobHistory.step_id, JobHistory.failedrundate, CAST(JobHistory.lasterror AS VARCHAR(250)) FROM msdb.dbo.sysjobs Jobs --Get the most recent Failure Datetime for each failed job within @FromDate and @ToDate CROSS APPLY (SELECT TOP 1 JobHistory.step_id,JobHistory.run_date, CASE JobHistory.run_date WHEN 0 THEN NULL ELSE CONVERT(DATETIME, STUFF(STUFF(CAST(JobHistory.run_date AS NCHAR(8)), 7, 0, ''-''), 5, 0, ''-'') + N'' '' + STUFF(STUFF(SUBSTRING(CAST(1000000 + JobHistory.run_time AS NCHAR(7)), 2, 6), 5, 0, '':''), 3, 0, '':''), 120) END AS [failedrundate] , [message] AS lasterror FROM msdb.dbo.sysjobhistory JobHistory WHERE run_status = 0 AND Jobs.job_id = JobHistory.job_id ORDER BY [failedrundate] DESC, [step_id] DESC) JobHistory WHERE Jobs.enabled = 1 AND JobHistory.failedrundate > CAST(DATEADD(DAY,-1,CAST(GETDATE() AS DATE)) AS DATETIME) --Check that each job has not succeeded since the last failure AND NOT EXISTS (SELECT [LastSuccessfulRunDate] FROM( SELECT CASE JobHistory.run_date WHEN 0 THEN NULL ELSE CONVERT(DATETIME, STUFF(STUFF(CAST(JobHistory.run_date AS NCHAR(8)), 7, 0, ''-''), 5, 0, ''-'') + N'' '' + STUFF(STUFF(SUBSTRING(CAST(1000000 + JobHistory.run_time AS NCHAR(7)), 2, 6), 5, 0, '':''), 3, 0, '':''), 120) END AS [LastSuccessfulRunDate] FROM msdb.dbo.sysjobhistory JobHistory WHERE run_status = 1 AND Jobs.job_id = JobHistory.job_id ) LastSuccessfulJobrun WHERE LastSuccessfulJobrun.[LastSuccessfulRunDate] > JobHistory.[failedrundate]) --Ensure that the job is not currently running AND NOT EXISTS (SELECT name FROM msdb.dbo.sysjobactivity JobActivity WHERE Jobs.job_id = JobActivity.job_id AND start_execution_date > DATEADD(MINUTE,-30,GETDATE()) AND stop_execution_date is null ) --Only show failed jobs where the Failed step is NOT configured to quit reporting success on error AND NOT EXISTS (SELECT 1 FROM msdb.dbo.sysjobsteps ReportingSuccessSteps WHERE Jobs.job_id = ReportingSuccessSteps.job_id AND JobHistory.step_id = ReportingSuccessSteps.step_id AND on_fail_action = 1 -- quit job reporting success ) ORDER BY name ASC IF NOT EXISTS (SELECT Servername FROM [Inspector].[FailedAgentJobs] WHERE Servername = @Servername) BEGIN INSERT INTO [Inspector].[FailedAgentJobs] (Servername,Log_Date,Jobname,LastStepFailed,LastFailedDate,LastError) VALUES(@Servername,GETDATE(),''No Failed Jobs present'',NULL,NULL,NULL) END END;'; IF OBJECT_ID('Inspector.LoginAttemptsInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[LoginAttemptsInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[LoginAttemptsInsert] AS BEGIN --Revision date: 28/06/2018 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DELETE FROM [Inspector].[LoginAttempts] WHERE Servername = @Servername; IF OBJECT_ID(''tempdb.dbo.#Errors'') IS NOT NULL DROP TABLE #Errors; CREATE TABLE #Errors ( Logdate DATETIME, Processinfo VARCHAR(30), Text VARCHAR(255) ); DECLARE @StartTime DATETIME = DATEADD(DAY,-1,GETDATE()) INSERT INTO #Errors ([Logdate],[Processinfo],[Text]) EXEC xp_readerrorlog 0, 1, N''FAILED'',N''login'',@StartTime,NULL; INSERT INTO [Inspector].[LoginAttempts] (Servername,Log_Date,Username,Attempts,LastErrorDate,LastError) SELECT @Servername, GETDATE(), REPLACE(LoginErrors.Username,'''''''',''''), CAST(LoginErrors.Attempts AS NVARCHAR(6)), LatestDate.Logdate, LatestDate.LastError FROM ( SELECT SUBSTRING(Text,PATINDEX(''%''''%''''%'',Text),CHARINDEX(''.'',Text)-(PATINDEX(''%''''%''''%'',Text))) as Username,Count(*) Attempts FROM #Errors Errors GROUP BY SUBSTRING(Text,PATINDEX(''%''''%''''%'',Text),CHARINDEX(''.'',Text)-(PATINDEX(''%''''%''''%'',Text))) ) LoginErrors CROSS APPLY (SELECT TOP 1 Logdate,Text as LastError FROM #Errors LatestDate WHERE LoginErrors.Username = SUBSTRING(Text,Patindex(''%''''%''''%'',Text),charindex(''.'',Text)-(Patindex(''%''''%''''%'',Text))) ORDER by Logdate DESC) LatestDate ORDER BY Attempts DESC IF NOT EXISTS (SELECT Servername FROM [Inspector].[LoginAttempts] WHERE Servername = @Servername) BEGIN INSERT INTO [Inspector].[LoginAttempts] (Servername,Log_Date,Username,Attempts,LastErrorDate,LastError) VALUES(@Servername,GETDATE(),''No Failed Logins present'',NULL,NULL,NULL) END END;'; IF OBJECT_ID('Inspector.JobOwnerInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[JobOwnerInsert] AS;'); SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' ALTER PROCEDURE [Inspector].[JobOwnerInsert] AS BEGIN --Revision date: 28/06/2018 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DECLARE @AgentjobOwnerExclusions VARCHAR(255) = (SELECT REPLACE([Value],'' '','''') FROM [Inspector].[Settings] WHERE [Description] = ''AgentJobOwnerExclusions'') DELETE FROM [Inspector].[JobOwner] WHERE Servername = @Servername; INSERT INTO [Inspector].[JobOwner] (Servername,Log_Date,Job_ID,Jobname) SELECT @Servername, GETDATE(), jobs.job_id, jobs.[name] FROM msdb.dbo.sysjobs jobs INNER join master.sys.syslogins logins ON jobs.owner_sid = logins.sid WHERE logins.name NOT IN ('+CASE WHEN @Compatibility = 0 THEN 'SELECT [StringElement] FROM master.dbo.fn_SplitString(@AgentjobOwnerExclusions,'','')' ELSE 'SELECT [value] FROM STRING_SPLIT(@AgentjobOwnerExclusions,'','')' END + ') AND jobs.enabled = 1 IF NOT EXISTS (SELECT Servername FROM [Inspector].[JobOwner] WHERE Servername = @Servername) BEGIN INSERT INTO [Inspector].[JobOwner] (Servername,Log_Date,Job_ID,Jobname) VALUES(@Servername,GETDATE(),NULL,''No Job Owner issues present'') END END;'; EXEC(@SQLStatement); IF OBJECT_ID('Inspector.TopFiveDatabasesInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[TopFiveDatabasesInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[TopFiveDatabasesInsert] AS BEGIN --Revision date: 28/06/2018 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DELETE FROM [Inspector].[TopFiveDatabases] WHERE Servername = @Servername; INSERT INTO [Inspector].[TopFiveDatabases] (Servername,Log_Date,Databasename,TotalSize_MB) SELECT TOP 5 @Servername, GETDATE(), Databasename, [TotalSize(MB)] FROM ( SELECT DBs.name AS Databasename, SUM((CAST(DBFiles.size AS BIGINT)*8)/1024 ) AS [TotalSize(MB)] FROM [sys].[master_files] DBFiles INNER JOIN sys.databases DBs ON DBFiles.database_id = DBs.database_id GROUP BY DBs.name ) Sizes ORDER BY [TotalSize(MB)] DESC END;'; IF OBJECT_ID('Inspector.BackupsCheckInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[BackupsCheckInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[BackupsCheckInsert] AS BEGIN --Revision date: 06/10/2021 DECLARE @Servername NVARCHAR(128) = @@SERVERNAME; DECLARE @FullBackupThreshold INT = (Select [Value] FROM [Inspector].[Settings] WHERE Description = ''FullBackupThreshold'') DECLARE @MaxFullThreshold INT; SET @FullBackupThreshold = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold] (@Servername,''BackupsCheck'',''FullBackupThreshold'') AS INT),8)); SET @MaxFullThreshold = (SELECT MAX(FullThreshold) FROM [Inspector].[BackupsCheckThresholds] WHERE [IsActive] = 1); IF (@FullBackupThreshold < @MaxFullThreshold) BEGIN SET @FullBackupThreshold = @MaxFullThreshold; END DELETE FROM [Inspector].[BackupsCheck] WHERE Servername = @Servername; IF SERVERPROPERTY(''IsHadrEnabled'') = 1 AND EXISTS (SELECT name FROM sys.availability_groups) BEGIN IF OBJECT_ID(''tempdb.dbo.#DatabaseList'') IS NOT NULL DROP TABLE #DatabaseList; CREATE TABLE #DatabaseList ( Database_id INT, Servername NVARCHAR(128), Log_Date DATETIME, Databasename NVARCHAR(128), AGname NVARCHAR(128), [State] TINYINT, Source_database_id INT, IsFullRecovery BIT, IsSystemDB BIT, backup_preference NVARCHAR(60) ); IF OBJECT_ID(''tempdb.dbo.#BackupAggregation'') IS NOT NULL DROP TABLE #BackupAggregation; CREATE TABLE #BackupAggregation ( Database_id INT, Databasename NVARCHAR(128), [Full] DATETIME, [Diff] DATETIME, [Log] DATETIME ); INSERT INTO #DatabaseList ([Database_id],[Servername],[Log_Date],[Databasename],[AGname],[State],[Source_database_id],[IsFullRecovery],[IsSystemDB],[backup_preference]) SELECT DISTINCT database_id, @Servername, GETDATE(), sys.databases.name, AG.name, [state], source_database_id, CASE WHEN recovery_model_desc = ''FULL'' THEN 1 WHEN recovery_model_desc IS NULL THEN 1 ELSE 0 END AS IsFullRecovery, CASE WHEN database_id <= 4 THEN 1 ELSE 0 END AS IsSystemDB, UPPER(automated_backup_preference_desc) AS backup_preference FROM sys.databases INNER JOIN sys.availability_replicas AR ON sys.databases.replica_id = AR.replica_id INNER JOIN sys.availability_groups AG ON AR.group_id = AG.group_id WHERE database_id != 2 AND [state] = 0 AND source_database_id IS NULL UNION ALL SELECT database_id, @Servername, GETDATE(), sys.databases.name, NULL, [state], source_database_id, CASE WHEN recovery_model_desc = ''FULL'' THEN 1 WHEN recovery_model_desc IS NULL THEN 1 ELSE 0 END AS IsFullRecovery, CASE WHEN database_id <= 4 THEN 1 ELSE 0 END AS IsSystemDB, N''Non AG'' AS backup_preference FROM sys.databases WHERE database_id != 2 AND [state] = 0 AND source_database_id IS NULL and replica_id is NULL ORDER BY database_id INSERT INTO #BackupAggregation ([Database_id],[Databasename],[Full],[Diff],[Log]) SELECT Database_id,database_name, [D], [I], [L] FROM (SELECT [Database_id],[backuplog].[database_name],[backuplog].[type],MAX([backuplog].[backup_finish_date]) AS backup_finish_date FROM msdb.dbo.backupset backuplog INNER JOIN #DatabaseList ON #DatabaseList.Databasename = backuplog.database_name WHERE backup_finish_date > DATEADD(DAY,-@FullBackupThreshold,CAST(GETDATE() AS DATE)) GROUP BY Database_id,backuplog.database_name,backuplog.type ) p PIVOT( MAX(backup_finish_date) FOR type IN ([D],[I],[L])) d ORDER BY Database_id ASC INSERT INTO [Inspector].[BackupsCheck] ([Servername],[Log_Date],[Databasename],[AGname],[FULL],[DIFF],[LOG],[IsFullRecovery],[IsSystemDB],[primary_replica],[backup_preference]) SELECT [Servername], [Log_Date], #DatabaseList.[Databasename], COALESCE(#DatabaseList.AGname,''Not in an AG'') AS AGname, ISNULL([Full],''19000101'') AS [FULL], ISNULL([Diff],''19000101'') AS [DIFF], ISNULL([Log],''19000101'') AS [LOG], [IsFullRecovery], [IsSystemDB], CASE WHEN #DatabaseList.[AGname] IS NULL THEN @@servername ELSE primary_replica END AS primary_replica, [backup_preference] FROM #DatabaseList LEFT JOIN #BackupAggregation ON #DatabaseList.Database_id = #BackupAggregation.Database_id LEFT JOIN (SELECT Groups.name,States.primary_replica FROM master.sys.dm_hadr_availability_group_states States INNER JOIN master.sys.availability_groups Groups ON States.group_id = Groups.group_id WHERE States.group_id = Groups.group_id GROUP BY Groups.name,States.primary_replica) AS primary_replicas ON primary_replicas.primary_replica = #DatabaseList.Servername AND #DatabaseList.AGname = primary_replicas.name WHERE ([State] = 0 AND Source_database_id IS NULL) END ELSE BEGIN INSERT INTO [Inspector].[BackupsCheck] ([Servername],[Log_Date],[Databasename],[AGname],[FULL],[DIFF],[LOG],[IsFullRecovery],[IsSystemDB],[primary_replica],[backup_preference]) SELECT DISTINCT @Servername,GETDATE(),dbs.name,@Servername +''(Non AG)'' AS AGname,ISNULL([D],''19000101''),ISNULL([I],''19000101''),ISNULL([L],''19000101''), CASE WHEN dbs.recovery_model_desc = ''FULL'' THEN 1 ELSE 0 END, CASE WHEN dbs.database_id <= 4 THEN 1 ELSE 0 END AS IsSystemDB, @Servername AS primary_replica, N''Non AG'' AS backup_preference FROM (SELECT [backuplog].[database_name],[backuplog].[type],MAX(backuplog.backup_finish_date) AS backup_finish_date FROM msdb.dbo.backupset backuplog WHERE backup_finish_date > DATEADD(DAY,-@FullBackupThreshold,CAST(GETDATE() AS DATE)) GROUP BY backuplog.database_name,backuplog.type ) p PIVOT( MAX(backup_finish_date) FOR type IN ([D],[I],[L])) d RIGHT JOIN sys.databases dbs ON d.database_name = dbs.name WHERE database_id != 2 AND [state] = 0 AND source_database_id IS NULL END END;'; IF OBJECT_ID('Inspector.DatabaseGrowthsInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatabaseGrowthsInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DatabaseGrowthsInsert] AS --Revision date: 06/06/2019 SET NOCOUNT ON; BEGIN DECLARE @Servername NVARCHAR(128)= @@Servername; DECLARE @LastUpdated DATETIME = GETDATE(); DECLARE @Retention INT = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@Servername, ''DatabaseGrowths'', ''DatabaseGrowthRetentionPeriodInDays'') AS INT), 90)); DECLARE @ScopeIdentity INT --Insert any databases that are present on the serverbut not present in [Inspector].[DatabaseFileSizes] IF SERVERPROPERTY(''IsHadrEnabled'') = 1 AND EXISTS (SELECT name FROM sys.availability_groups) BEGIN INSERT INTO [Inspector].[DatabaseFileSizes] ([Servername], [Database_id], [Database_name], [OriginalDateLogged], [OriginalSize_MB], [Type_desc], [File_id], [Filename], [PostGrowthSize_MB], [GrowthRate], [Is_percent_growth], [NextGrowth] ) SELECT @Servername, [Masterfiles].[database_id], DB_NAME([Masterfiles].[database_id]) AS [Database_name], @LastUpdated AS [OriginalDateLogged], CAST([Masterfiles].[size] AS BIGINT) * 8 / 1024 AS [OriginalSize_MB], [Masterfiles].[type_desc], [Masterfiles].[file_id], [Masterfiles].[physical_name], CAST([Masterfiles].[size] AS BIGINT) * 8 / 1024 AS [PostGrowthSize_MB], CASE [Masterfiles].[is_percent_growth] WHEN 0 THEN CASE --handle divide by zero by defaulting to a 1 WHEN growth = 0 THEN 1 ELSE ([Masterfiles].[growth] * 8) / 1024 END WHEN 1 THEN CASE --handle divide by zero by defaulting to a 1 WHEN (((CAST([size] AS BIGINT) * 8) / 1024) * [growth]) / 100 = 0 THEN 1 ELSE (((CAST([size] AS BIGINT) * 8) / 1024) * [growth]) / 100 END END AS [GrowthRate_MB], [Masterfiles].[is_percent_growth], CASE [Masterfiles].[is_percent_growth] WHEN 0 THEN CASE --handle divide by zero by defaulting to a 1 WHEN growth = 0 THEN ((CAST([size] AS BIGINT) * 8)/ 1024) + 1 ELSE ((CAST([size] AS BIGINT) * 8) / 1024) + ([growth] * 8) / 1024 END WHEN 1 THEN CASE WHEN ((CAST([size] AS BIGINT) * 8) / 1024) + (((CAST([size] AS BIGINT) * 8) / 1024) * [growth]) / 100 = 0 THEN ((CAST([size] AS BIGINT) * 8)/ 1024) + 1 ELSE ((CAST([size] AS BIGINT) * 8) / 1024) + (((CAST([size] AS BIGINT) * 8) / 1024) * [growth]) / 100 END END [NextGrowth] FROM [sys].[master_files] [Masterfiles] LEFT JOIN ( SELECT DB_ID([ADC].[database_name]) AS [Database_ID] FROM [sys].[dm_hadr_availability_group_states] [ST] INNER JOIN [sys].[availability_databases_cluster] [ADC] ON [ST].[group_id] = [ADC].[group_id] WHERE [primary_replica] = @@Servername ) [DatabaseList] ON [DatabaseList].[Database_ID] = [Masterfiles].[database_id] WHERE [Masterfiles].[database_id] > 3 AND [type_desc] != ''LOG'' AND NOT EXISTS ( SELECT [Database_id] FROM [Inspector].[DatabaseFileSizes] [DatabaseFileSizes] WHERE [Servername] = @Servername AND DB_NAME([Masterfiles].[database_id]) = [DatabaseFileSizes].[Database_name] AND [Masterfiles].[file_id] = [DatabaseFileSizes].[File_id] ) END ELSE BEGIN INSERT INTO [Inspector].[DatabaseFileSizes] ([Servername], [Database_id], [Database_name], [OriginalDateLogged], [OriginalSize_MB], [Type_desc], [File_id], [Filename], [PostGrowthSize_MB], [GrowthRate], [Is_percent_growth], [NextGrowth] ) SELECT @Servername, [Masterfiles].[database_id], DB_NAME([Masterfiles].[database_id]) AS [Database_name], GETDATE() AS [OriginalDateLogged], CAST([Masterfiles].[size] AS BIGINT) * 8 / 1024 AS [OriginalSize_MB], [Masterfiles].[type_desc], [Masterfiles].[file_id], [Masterfiles].[physical_name], CAST([Masterfiles].[size] AS BIGINT) * 8 / 1024 AS [PostGrowthSize_MB], CASE [Masterfiles].[is_percent_growth] WHEN 0 THEN CASE --handle divide by zero by defaulting to a 1 WHEN growth = 0 THEN 1 ELSE ([Masterfiles].[growth] * 8) / 1024 END WHEN 1 THEN CASE --handle divide by zero by defaulting to a 1 WHEN (((CAST([size] AS BIGINT) * 8) / 1024) * [growth]) / 100 = 0 THEN 1 ELSE (((CAST([size] AS BIGINT) * 8) / 1024) * [growth]) / 100 END END AS [GrowthRate_MB], [Masterfiles].[is_percent_growth], CASE [Masterfiles].[is_percent_growth] WHEN 0 THEN CASE --handle divide by zero by defaulting to a 1 WHEN growth = 0 THEN ((CAST([size] AS BIGINT) * 8)/ 1024) + 1 ELSE ((CAST([size] AS BIGINT) * 8) / 1024) + ([growth] * 8) / 1024 END WHEN 1 THEN CASE WHEN ((CAST([size] AS BIGINT) * 8) / 1024) + (((CAST([size] AS BIGINT) * 8) / 1024) * [growth]) / 100 = 0 THEN ((CAST([size] AS BIGINT) * 8)/ 1024) + 1 ELSE ((CAST([size] AS BIGINT) * 8) / 1024) + (((CAST([size] AS BIGINT) * 8) / 1024) * [growth]) / 100 END END [NextGrowth] FROM [sys].[master_files] [Masterfiles] WHERE [Masterfiles].[database_id] > 3 AND [type_desc] != ''LOG'' AND NOT EXISTS ( SELECT [Database_id] FROM [Inspector].[DatabaseFileSizes] [DatabaseFileSizes] WHERE [Servername] = @Servername AND DB_NAME([Masterfiles].[database_id]) = [DatabaseFileSizes].[Database_name] AND [Masterfiles].[file_id] = [DatabaseFileSizes].[File_id] ) END --Remove any databases that have been dropped from SQL but still present in [Inspector].[DatabaseFileSizes] DELETE [Sizes] FROM [Inspector].[DatabaseFileSizes] [Sizes] LEFT JOIN [sys].[databases] [DatabasesList] ON [Sizes].[Database_name] = [DatabasesList].[name] COLLATE DATABASE_DEFAULT WHERE [Sizes].[Servername] = @Servername AND [DatabasesList].[database_id] IS NULL; --Ensure that the Database_Id column is synced in the base table as a database may have been dropped and restored as a new Database_id UPDATE [Sizes] SET [Database_id] = [DatabasesList].[database_id], [LastUpdated] = @LastUpdated FROM [Inspector].[DatabaseFileSizes] [Sizes] INNER JOIN [sys].[databases] [DatabasesList] ON [Sizes].[Database_name] = [DatabasesList].[name] COLLATE DATABASE_DEFAULT WHERE [Sizes].[Servername] = @Servername AND [DatabasesList].[database_id] != [Sizes].[Database_id]; --Keep the base table in sync by checking if the growth rates have changed - if they have then update the base table UPDATE [Sizes] SET [GrowthRate] = [GrowthCheck].[GrowthRate_MB], [Is_percent_growth] = [GrowthCheck].[is_percent_growth], [NextGrowth] = ([Sizes].[PostGrowthSize_MB]+[GrowthCheck].[GrowthRate_MB]), [LastUpdated] = @LastUpdated FROM ( SELECT [Masterfiles].[database_id], [Masterfiles].[file_id], CASE [Masterfiles].[is_percent_growth] WHEN 0 THEN CASE --handle divide by zero by defaulting to a 1 WHEN growth = 0 THEN 1 ELSE ([Masterfiles].[growth] * 8) / 1024 END WHEN 1 THEN CASE --handle divide by zero by defaulting to a 1 WHEN (((CAST([size] AS BIGINT) * 8) / 1024) * [growth]) / 100 = 0 THEN 1 ELSE (((CAST([size] AS BIGINT) * 8) / 1024) * [growth]) / 100 END END AS [GrowthRate_MB], --IN MB , The physical value expressed as a number [Masterfiles].[is_percent_growth] FROM [sys].[master_files] [Masterfiles] INNER JOIN [sys].[databases] [DatabasesList] ON [Masterfiles].[database_id] = [DatabasesList].[database_id] WHERE [Masterfiles].[database_id] > 3 AND [type_desc] != ''LOG'' AND [DatabasesList].state = 0 ) [GrowthCheck] INNER JOIN [Inspector].[DatabaseFileSizes] [Sizes] ON [GrowthCheck].[database_id] = [Sizes].[Database_id] AND [Sizes].[File_id] = [GrowthCheck].[file_id] WHERE(([GrowthCheck].[GrowthRate_MB] != [Sizes].[GrowthRate]) OR ([GrowthCheck].[is_percent_growth] != [Sizes].[Is_percent_growth])) AND [Servername] = @Servername; --If database has shrunk in size update the PostGrowthSize_MB column and NextGrowth UPDATE [Sizes] SET [PostGrowthSize_MB] = [ShrunkDatabases].[size], [NextGrowth] = ([ShrunkDatabases].[size]+[Sizes].[GrowthRate]), [LastUpdated] = @LastUpdated FROM ( SELECT [Masterfiles].[database_id], [Masterfiles].[file_id], (CAST([Masterfiles].[size] AS BIGINT) * 8) / 1024 AS size FROM [sys].[master_files] [Masterfiles] INNER JOIN [sys].[databases] [DatabasesList] ON [Masterfiles].[database_id] = [DatabasesList].[database_id] WHERE [Masterfiles].[database_id] > 3 AND [type_desc] != ''LOG'' AND [DatabasesList].state = 0 ) [ShrunkDatabases] INNER JOIN [Inspector].[DatabaseFileSizes] [Sizes] ON [ShrunkDatabases].[database_id] = [Sizes].[Database_id] AND [Sizes].[File_id] = [ShrunkDatabases].[file_id] WHERE [ShrunkDatabases].[size] < [PostGrowthSize_MB] AND [Servername] = @Servername; --Log the Database Growth event INSERT INTO [Inspector].[DatabaseFileSizeHistory] ([Servername], [Database_id], [Database_name], [Log_Date], [Type_Desc], [File_id], [Drive], [FileName], [PreGrowthSize_MB], [GrowthRate_MB], [GrowthIncrements], [PostGrowthSize_MB] ) SELECT [DatabaseFileSizes].[Servername], [Masterfiles].[database_id], DB_NAME([Masterfiles].[database_id]) AS [Database_name], @LastUpdated AS [Log_Date], [Masterfiles].[type_desc], [Masterfiles].[file_id], CAST(UPPER(volumestats.volume_mount_point) AS NVARCHAR(128)) AS Drive, RIGHT([Masterfiles].[physical_name],CHARINDEX(''\'',REVERSE([Masterfiles].[physical_name]))-1) AS [Filename], --Get the Filename [DatabaseFileSizes].[PostGrowthSize_MB], --PostGrowth size is the Last recorded database size after a growth event [DatabaseFileSizes].[GrowthRate], (((CAST([Masterfiles].[size] AS BIGINT) * 8) / 1024 - [DatabaseFileSizes].[PostGrowthSize_MB]) / [DatabaseFileSizes].[GrowthRate]) AS [TotalGrowthIncrements], --IF Growth is in Percent then this will be calculated based on the Current DB size Less Originally logged size , Divided by the Growth percentage based on the original database size (CAST([Masterfiles].[size] AS BIGINT) * 8) / 1024 AS [CurrentSize_MB] --Next approx Growth interval in MB FROM [Inspector].[DatabaseFileSizes] [DatabaseFileSizes] INNER JOIN [sys].[master_files] [Masterfiles] ON [Masterfiles].[database_id] = [DatabaseFileSizes].[Database_id] AND [DatabaseFileSizes].[File_id] = [Masterfiles].[file_id] CROSS APPLY sys.dm_os_volume_stats([Masterfiles].[database_id],[Masterfiles].[file_id]) volumestats WHERE [NextGrowth] <= (CAST([Masterfiles].[size] AS BIGINT) * 8) / 1024 AND [DatabaseFileSizes].[Servername] = @Servername AND NOT EXISTS ( SELECT GrowthID FROM [Inspector].[DatabaseFileSizeHistory] ExistingRecord WHERE [Servername] = @Servername AND DB_NAME([Masterfiles].[database_id]) = [Database_name] AND CAST([Log_Date] AS DATE) = CAST(GETDATE() AS DATE) ); --Ensure that there has not been any growths logged for today before recording as this will affect thresholds. --(this allows the collection to be ran without worrying that the growths will be logged prematurely); SELECT @ScopeIdentity = MAX(GrowthID) FROM [Inspector].[DatabaseFileSizeHistory] WHERE Servername = @Servername AND Log_Date = @LastUpdated; IF (@ScopeIdentity IS NOT NULL) --IF Growths have just been inserted BEGIN --Double check the databases sizes in the base table are correct and update as required UPDATE [Sizes] SET [PostGrowthSize_MB] = (CAST([Masterfiles].[size] AS BIGINT) * 8) / 1024, [LastUpdated] = @LastUpdated FROM [sys].[master_files] [Masterfiles] INNER JOIN [Inspector].[DatabaseFileSizes] [Sizes] ON [Masterfiles].[database_id] = [Sizes].[Database_id] AND [Sizes].[File_id] = [Masterfiles].[file_id] WHERE [Masterfiles].[database_id] > 3 AND ((CAST([Masterfiles].[size] AS BIGINT) * 8) / 1024 != [Sizes].[PostGrowthSize_MB]) AND [Servername] = @Servername; --Set Next growth size for all Databases on this server which have grown UPDATE [Inspector].[DatabaseFileSizes] SET [NextGrowth] = ([PostGrowthSize_MB] + [GrowthRate]), [LastUpdated] = @LastUpdated WHERE [NextGrowth] <= [PostGrowthSize_MB] AND [Servername] = @Servername; END --Clean up the history for growths older than @Retention in days DELETE FROM [Inspector].[DatabaseFileSizeHistory] WHERE [Log_Date] < DATEADD(DAY,-@Retention,GETDATE()) AND [Servername] = @Servername; END;'; IF OBJECT_ID('Inspector.DatabaseOwnershipInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatabaseOwnershipInsert] AS;'); SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' ALTER PROCEDURE [Inspector].[DatabaseOwnershipInsert] AS BEGIN --Revision date: 28/06/2018 DECLARE @Servername NVARCHAR(128) = @@SERVERNAME; DECLARE @DatabaseOwnerExclusions NVARCHAR(255) = (SELECT REPLACE(Value,'' '','''') from Inspector.Settings WHERE Description = ''DatabaseOwnerExclusions''); DELETE FROM [Inspector].[DatabaseOwnership] WHERE Servername = @Servername; IF SERVERPROPERTY(''IsHadrEnabled'') = 1 AND EXISTS (SELECT name FROM sys.availability_groups) BEGIN INSERT INTO Inspector.DatabaseOwnership ([Servername],[Log_Date],[AGname],[Database_name],[Owner]) SELECT @Servername, GETDATE(), AG.name as AGname, Databases.[name], COALESCE(SUSER_SNAME(Databases.[Owner_sid]),''Blank'') FROM sys.dm_hadr_availability_group_states ST INNER JOIN master.sys.availability_groups AG ON ST.group_id = AG.group_id INNER JOIN sys.availability_databases_cluster ADC ON AG.group_id = ADC.group_id INNER JOIN sys.databases Databases ON Databases.name = ADC.database_name WHERE primary_replica = @@Servername AND Databases.owner_sid NOT IN ('+CASE WHEN @Compatibility = 0 THEN 'SELECT SUSER_SID([StringElement]) FROM master.dbo.fn_SplitString(@DatabaseOwnerExclusions,'','')' ELSE 'SELECT SUSER_SID([value]) FROM STRING_SPLIT(@DatabaseOwnerExclusions,'','')' END+ ') AND Databases.state = 0 UNION ALL SELECT @Servername, GETDATE(), ''Not in an AG'' as AGname, Databases.[name], COALESCE(SUSER_SNAME(Databases.[owner_sid]),''Blank'') FROM sys.databases Databases WHERE replica_id IS NULL AND Databases.owner_sid NOT IN ('+CASE WHEN @Compatibility = 0 THEN 'SELECT SUSER_SID([StringElement]) FROM master.dbo.fn_SplitString(@DatabaseOwnerExclusions,'','')' ELSE 'SELECT SUSER_SID([value]) FROM STRING_SPLIT(@DatabaseOwnerExclusions,'','')' END+ ') AND Databases.state = 0 AND source_database_id IS NULL ORDER BY Databases.name ASC END ELSE BEGIN INSERT INTO Inspector.DatabaseOwnership ([Servername],[Log_Date],[AGname],[Database_name],[Owner]) SELECT @Servername, GETDATE(), ''N/A'' as AGname, Databases.[name], COALESCE(SUSER_SNAME(Databases.[owner_sid]),''Blank'') FROM sys.databases Databases WHERE replica_id IS NULL AND Databases.owner_sid NOT IN ('+CASE WHEN @Compatibility = 0 THEN 'SELECT SUSER_SID([StringElement]) FROM master.dbo.fn_SplitString(@DatabaseOwnerExclusions,'','')' ELSE 'SELECT SUSER_SID([value]) FROM STRING_SPLIT(@DatabaseOwnerExclusions,'','')' END+ ') AND Databases.state = 0 AND source_database_id IS NULL ORDER BY Databases.name ASC END IF NOT EXISTS (SELECT Servername FROM [Inspector].[DatabaseOwnership] WHERE Servername = @Servername) BEGIN INSERT INTO [Inspector].[DatabaseOwnership] ([Servername],[Log_Date],[AGname],[Database_name],[Owner]) VALUES(@Servername,GETDATE(),NULL,''No Database Ownership issues present'',NULL) END END;' EXEC(@SQLStatement); IF OBJECT_ID('Inspector.BackupSizesByDayInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[BackupSizesByDayInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[BackupSizesByDayInsert] AS BEGIN --Revision date: 28/06/2018 DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DELETE FROM [Inspector].[BackupSizesByDay] WHERE Servername = @@Servername; INSERT INTO [Inspector].[BackupSizesByDay] ([Servername],[Log_Date],[DayOfWeek],[CastedDate],[TotalSizeInBytes]) SELECT @Servername, GETDATE(), [DayOfWeek] , [CastedDate], [TotalSizeInBytes] FROM ( SELECT DATENAME(WEEKDAY,backup_start_date) AS [DayOfWeek], CAST(backup_start_date AS DATE) AS [CastedDate] , SUM(COALESCE(compressed_backup_size,backup_size)) AS [TotalSizeInBytes] FROM msdb.dbo.backupset WHERE backup_start_date >= DATEADD(DAY,-7,CAST(GETDATE() AS DATE)) GROUP BY DATENAME(WEEKDAY,backup_start_date) ,CAST(backup_start_date AS DATE) ) as BackupSizesbyDay; IF NOT EXISTS (SELECT Servername FROM [Inspector].[BackupSizesByDay] WHERE Servername = @Servername) BEGIN INSERT INTO [Inspector].[BackupSizesByDay] ([Servername],[Log_Date],[DayOfWeek],[CastedDate],[TotalSizeInBytes]) VALUES(@Servername,NULL,NULL,NULL,NULL) END END;'; IF OBJECT_ID('Inspector.DatabaseSettingsInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatabaseSettingsInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DatabaseSettingsInsert] AS BEGIN --Revision date: 28/06/2018 DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DECLARE @LogDate DATETIME = GETDATE() DELETE FROM [Inspector].[DatabaseSettings] WHERE Servername = @Servername INSERT INTO [Inspector].[DatabaseSettings] (Servername,Log_Date,Setting,Description,Total) SELECT @Servername, @LogDate, ''Collation_name'', ISNULL(collation_name,''None'') , COUNT(collation_name) FROM sys.databases GROUP BY collation_name INSERT INTO [Inspector].[DatabaseSettings] (Servername,Log_Date,Setting,Description,Total) SELECT @Servername, @LogDate, ''is_auto_close_on'', CASE is_auto_close_on WHEN 1 THEN ''Enabled'' ELSE ''Disabled'' END , COUNT(is_auto_close_on) FROM sys.databases GROUP BY is_auto_close_on INSERT INTO [Inspector].[DatabaseSettings] (Servername,Log_Date,Setting,Description,Total) SELECT @Servername, @LogDate, ''is_auto_shrink_on'', CASE is_auto_shrink_on WHEN 1 THEN ''Enabled'' ELSE ''Disabled'' END , COUNT(is_auto_shrink_on) FROM sys.databases GROUP BY is_auto_shrink_on INSERT INTO [Inspector].[DatabaseSettings] (Servername,Log_Date,Setting,Description,Total) SELECT @Servername, @LogDate, ''is_auto_update_stats_on'', CASE is_auto_update_stats_on WHEN 1 THEN ''Enabled'' ELSE ''Disabled'' END , COUNT(is_auto_update_stats_on) FROM sys.databases GROUP BY is_auto_update_stats_on INSERT INTO [Inspector].[DatabaseSettings] (Servername,Log_Date,Setting,Description,Total) SELECT @Servername, @LogDate, ''is_read_only'', CASE is_read_only WHEN 1 THEN ''Enabled'' ELSE ''Disabled'' END , COUNT(is_read_only) FROM sys.databases GROUP BY is_read_only INSERT INTO [Inspector].[DatabaseSettings] (Servername,Log_Date,Setting,Description,Total) SELECT @Servername, @LogDate, ''user_access_desc'', user_access_desc, COUNT(user_access_desc) FROM sys.databases GROUP BY user_access_desc INSERT INTO [Inspector].[DatabaseSettings] (Servername,Log_Date,Setting,Description,Total) SELECT @Servername, @LogDate, ''compatibility_level'', [compatibility_level], COUNT([compatibility_level]) FROM sys.databases GROUP BY [compatibility_level] INSERT INTO [Inspector].[DatabaseSettings] (Servername,Log_Date,Setting,Description,Total) SELECT @Servername, @LogDate, ''recovery_model_desc'', recovery_model_desc, COUNT(recovery_model_desc) FROM sys.databases GROUP BY recovery_model_desc END;'; IF OBJECT_ID('Inspector.ServerSettingsInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[ServerSettingsInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[ServerSettingsInsert] AS BEGIN --Revision date: 21/05/2021 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DECLARE @LogDate DATETIME = GETDATE() /* INSERT any configuration options not currently in the Inspector table */ INSERT INTO [Inspector].[ServerSettings] ([Servername],[Log_Date],[configuration_id],[Setting],[value_in_use],[LastUpdated]) SELECT @Servername, @LogDate, [configuration_id], CAST([name] AS NVARCHAR(128)), CAST([value_in_use] AS INT), @LogDate AS LastUpdated FROM sys.configurations WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[ServerSettings] WHERE [ServerSettings].[configuration_id] = [configurations].[configuration_id] AND [ServerSettings].[Servername] = @Servername ); /* UPDATE Lastupdated to say we just checked if config has changed and update value_in_use */ UPDATE ss SET [value_in_use] = CAST([configurations].[value_in_use] AS INT), [LastUpdated] = @LogDate FROM [Inspector].[ServerSettings] ss INNER JOIN sys.configurations ON [ss].[configuration_id] = [configurations].[configuration_id] AND [ss].[Servername] = @Servername; END;'; IF OBJECT_ID('Inspector.InstanceStartInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[InstanceStartInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[InstanceStartInsert] AS BEGIN --Revision date: 02/07/2018 DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DECLARE @LogDate DATETIME = GETDATE() DELETE FROM [Inspector].[InstanceStart] WHERE Servername = @Servername; INSERT INTO [Inspector].[InstanceStart] ([Servername],[Log_Date],[InstanceStart]) SELECT @Servername, @LogDate, [create_date] FROM sys.databases WHERE name = ''tempdb'' END;'; IF OBJECT_ID('Inspector.InstanceVersionInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[InstanceVersionInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[InstanceVersionInsert] AS BEGIN --Revision date: 29/05/2021 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME; DECLARE @PhysicalServername NVARCHAR(128) = CAST(SERVERPROPERTY(''ComputerNamePhysicalNetBIOS'') AS NVARCHAR(128)); DECLARE @Version NVARCHAR(20) = CAST(SERVERPROPERTY(''ProductVersion'') AS NVARCHAR(20)); DECLARE @Edition NVARCHAR(50) = CAST(SERVERPROPERTY(''Edition'') AS NVARCHAR(50)); --Check for version and/or edition change IF EXISTS(SELECT 1 FROM [Inspector].[InstanceVersion] WHERE Servername = @Servername) BEGIN --If the major version has changed we only want to advise of that change, if the major version has not changed --then check for a minor version change and advise of the change. INSERT INTO [Inspector].[InstanceVersionHistory] ([Servername], [Log_Date], [CollectionDatetime], [VersionNo], [Edition]) SELECT @Servername, [Log_Date], GETDATE(), [VersionNo], [Edition] FROM ( SELECT [Log_Date], CASE WHEN [VersionNo] != @Version THEN CASE WHEN PARSENAME([VersionNo],4) != PARSENAME(@Version,4) THEN N''Major Version changed from ['' + PARSENAME([VersionNo],4) + N''] to ['' + PARSENAME(@Version,4) + N''] '' + ISNULL(N'' ''+CAST(SERVERPROPERTY(''ProductLevel'') AS NVARCHAR(6)),'''') + ISNULL(N'' ''+CAST(SERVERPROPERTY(''ProductUpdateLevel'') AS NVARCHAR(6)),'''') WHEN PARSENAME([VersionNo],2) != PARSENAME(@Version,2) THEN N''Minor Version changed from ['' + PARSENAME([VersionNo],2) + N''] to ['' + PARSENAME(@Version,2) + N''] '' + ISNULL(N'' ''+CAST(SERVERPROPERTY(''ProductLevel'') AS NVARCHAR(6)),'''') + ISNULL(N'' ''+CAST(SERVERPROPERTY(''ProductUpdateLevel'') AS NVARCHAR(6)),'''') END ELSE NULL END AS [VersionNo], CASE WHEN [Edition] != @Edition THEN N''Edition Changed from ['' + [Edition] + N''] to ['' + @Edition + N'']'' ELSE NULL END AS [Edition] FROM (--Split Version and Edition into two columns SELECT [Log_Date], SUBSTRING([VersionInfo],0,CHARINDEX('' - '',[VersionInfo])) AS VersionNo, SUBSTRING([VersionInfo],CHARINDEX('' - '',[VersionInfo])+3,LEN([VersionInfo])-CHARINDEX('' - '',[VersionInfo])) AS Edition FROM [Inspector].[InstanceVersion] WHERE Servername = @Servername ) AS LastLoggedVersionInfo ) AS VersionCheck WHERE ([VersionNo] IS NOT NULL OR [Edition] IS NOT NULL) AND NOT EXISTS (SELECT 1 FROM [Inspector].[InstanceVersionHistory] WHERE Servername = @Servername AND CAST(VersionCheck.[Log_Date] AS DATE) = CAST([InstanceVersionHistory].[Log_Date] AS DATE) AND ([VersionNo] = [VersionCheck].[VersionNo] OR [Edition] = [VersionCheck].[Edition])) END DELETE FROM [Inspector].[InstanceVersion] WHERE Servername = @Servername; INSERT INTO [Inspector].[InstanceVersion] ([Servername], [PhysicalServername], [Log_Date], [VersionInfo]) SELECT @Servername, @PhysicalServername, GETDATE(), @Version + N'' - '' + @Edition /* Log server info */ DELETE FROM [Inspector].[ServerInfo] WHERE Servername = @Servername; INSERT INTO [Inspector].[ServerInfo] ([Servername], [Log_Date], [cpu_count], [hyperthread_count], [physical_memory_gb], [scheduler_count], [affinity_type_desc], [machine_type]) SELECT @Servername, GETDATE(), cpu_count, hyperthread_ratio as hyperthread_count, (physical_memory_kb/1024)/1000 AS physical_memory_gb, scheduler_count, affinity_type_desc, CASE WHEN virtual_machine_type_desc = N''HYPERVISOR'' THEN N''Virtual'' ELSE N''Physical'' END FROM sys.dm_os_sys_info; END;'; IF OBJECT_ID('Inspector.SuspectPagesInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[SuspectPagesInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[SuspectPagesInsert] AS BEGIN --Revision date: 30/07/2018 DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DECLARE @LogDate DATETIME = GETDATE() DELETE FROM [Inspector].[SuspectPages] WHERE Servername = @Servername INSERT INTO [Inspector].[SuspectPages] ([Servername],[Log_Date],[Databasename],[file_id],[page_id],[event_type],[error_count],[last_update_date]) SELECT @Servername, @LogDate, DB_NAME([database_id]), [file_id], [page_id], [event_type], [error_count], [last_update_date] FROM msdb.dbo.suspect_pages IF NOT EXISTS (SELECT Servername FROM [Inspector].[SuspectPages] WHERE Servername = @Servername) BEGIN INSERT INTO [Inspector].[SuspectPages] ([Servername],[Log_Date],[Databasename],[file_id],[page_id],[event_type],[error_count],[last_update_date]) VALUES(@Servername,GETDATE(),NULL,NULL,NULL,NULL,NULL,NULL) END END;'; IF OBJECT_ID('Inspector.AGDatabasesInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[AGDatabasesInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[AGDatabasesInsert] AS BEGIN --Revision date: 10/08/2018 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME; DECLARE @AGEnabled BIT; IF SERVERPROPERTY(''IsHadrEnabled'') = 1 AND EXISTS (SELECT name FROM sys.availability_groups) BEGIN SET @AGEnabled = 1 END ELSE BEGIN SET @AGEnabled = 0 END --Delete databases from the table when the state is not online OR Database is no longer on the server. DELETE FROM [Inspector].[AGDatabases] WHERE AGDatabases.Servername = @Servername AND (EXISTS (SELECT 1 FROM sys.databases DBs WHERE AGDatabases.Databasename = DBs.name COLLATE DATABASE_DEFAULT AND AGDatabases.Servername = @Servername AND state != 0) OR NOT EXISTS (SELECT 1 FROM sys.databases DBs WHERE AGDatabases.Databasename = DBs.name COLLATE DATABASE_DEFAULT AND AGDatabases.Servername = @Servername)); --INSERT databases missing from the table and assume they should be joined to an AG. INSERT INTO [Inspector].[AGDatabases] ([Servername], [Log_Date], [LastUpdated], [Databasename], [Is_AG], [Is_AGJoined]) SELECT @Servername, GETDATE(), GETDATE(), [name], @AGEnabled, CASE WHEN AGDBs.database_name IS NULL THEN 0 ELSE 1 END FROM sys.databases DBs LEFT JOIN (SELECT JoinedDBs.group_id,database_name FROM sys.availability_databases_cluster JoinedDBs WHERE EXISTS (SELECT 1 FROM sys.availability_groups Groups WHERE JoinedDBs.group_id = Groups.group_id) ) AS AGDBs ON DBs.name COLLATE DATABASE_DEFAULT = AGDBs.database_name LEFT JOIN sys.availability_replicas AGReplicas ON AGDBs.group_id = AGReplicas.group_id AND AGReplicas.replica_server_name = @Servername WHERE DBs.database_id > 4 AND state = 0 AND source_database_id IS NULL AND NOT EXISTS (SELECT 1 FROM [Inspector].[AGDatabases] WHERE Databasename = DBs.name COLLATE DATABASE_DEFAULT AND Servername = @Servername); --Update Is_AGJoined UPDATE DBs SET [Is_AGJoined] = CASE WHEN [AGReplicas].[replica_server_name] IS NULL THEN 0 ELSE 1 END, [LastUpdated] = GETDATE() FROM [Inspector].[AGDatabases] DBs LEFT JOIN (SELECT JoinedDBs.group_id,database_name FROM sys.availability_databases_cluster JoinedDBs WHERE EXISTS (SELECT 1 FROM sys.availability_groups Groups WHERE JoinedDBs.group_id = Groups.group_id) ) AS AGDBs ON DBs.Databasename COLLATE DATABASE_DEFAULT = AGDBs.database_name LEFT JOIN sys.availability_replicas AGReplicas ON AGDBs.group_id = AGReplicas.group_id AND AGReplicas.replica_server_name = @Servername WHERE DBs.Servername = @Servername; END'; IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[LongRunningTransactionsUpdate]') AND type in (N'P', N'PC')) BEGIN EXEC dbo.sp_executesql @statement = N'CREATE PROCEDURE [Inspector].[LongRunningTransactionsUpdate] AS' END EXEC sp_executesql N'ALTER PROCEDURE [Inspector].[LongRunningTransactionsUpdate] ( @Servername NVARCHAR(128), @Debug BIT = 0 ) AS SET NOCOUNT ON; DECLARE @Loginname NVARCHAR(128); DECLARE @Hostname NVARCHAR(128); DECLARE @Programname NVARCHAR(128); DECLARE @DurationInSeconds INT; DECLARE @ThresholdType VARCHAR(128); DECLARE @UPDSQLStmt NVARCHAR(1000); DECLARE @UPDSQLStmtWhere NVARCHAR(1000); DECLARE @DELSQLStmt NVARCHAR(1000); DECLARE @DELSQLStmtWhere NVARCHAR(1000); DECLARE @GlobalThreshold INT = ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@Servername,NULL,''LongRunningTransactionThreshold'') AS INT),300); /* Evaluate each threshold row and update the Transaction table with the Threhold type applied where applicable */ DECLARE Threshold_cur CURSOR LOCAL FAST_FORWARD FOR SELECT [login_name], [host_name], [program_name], [DurationInSeconds] FROM [Inspector].[LongRunningTransactionsThresholds] WHERE [IsActive] = 1 AND [Servername] = @Servername; OPEN Threshold_cur FETCH NEXT FROM Threshold_cur INTO @Loginname, @Hostname, @Programname, @DurationInSeconds WHILE @@FETCH_STATUS = 0 BEGIN SET @UPDSQLStmtWhere = N''''; SET @ThresholdType = N''''; SET @UPDSQLStmt = N'' /* Statement generated by procedure: [Inspector].[LongRunningTransactionsUpdate] */ UPDATE [Inspector].[LongRunningTransactions] SET [ThresholdType] = @ThresholdType WHERE [Servername] = @Servername AND [transaction_begin_time] < DATEADD(SECOND,-@DurationInSeconds,[Log_Date]) AND [session_id] IS NOT NULL''; IF (@Loginname IS NOT NULL) BEGIN SET @ThresholdType += '' | Loginname''; SET @UPDSQLStmtWhere += N'' AND [login_name] = @Loginname''; END IF (@Hostname IS NOT NULL) BEGIN SET @ThresholdType += '' | Hostname''; SET @UPDSQLStmtWhere += N'' AND [host_name] = @Hostname''; END IF (@Programname IS NOT NULL) BEGIN SET @ThresholdType += '' | Programname''; SET @UPDSQLStmtWhere += N'' AND [program_name] = @Programname''; END SET @ThresholdType = STUFF(@ThresholdType,1,3,''''); SET @ThresholdType = @ThresholdType + '' - '' +CAST(@DurationInSeconds AS VARCHAR(10)) +'' Seconds '' +CASE WHEN @DurationInSeconds > 300 THEN ''(''+CAST(CAST(CAST(@DurationInSeconds AS MONEY)/60.00 AS MONEY) AS VARCHAR(10))+'' Minutes)'' ELSE '''' END; SET @UPDSQLStmt += @UPDSQLStmtWhere; IF(@Debug = 1) BEGIN RAISERROR(''%s'',0,0,@UPDSQLStmt) WITH NOWAIT; END EXEC sp_executesql @UPDSQLStmt, N''@Servername NVARCHAR(128), @Loginname NVARCHAR(128), @Hostname NVARCHAR(128), @Programname NVARCHAR(128), @DurationInSeconds INT, @ThresholdType VARCHAR(128)'', @Servername = @Servername, @Loginname = @Loginname, @Hostname = @Hostname, @Programname = @Programname, @DurationInSeconds = @DurationInSeconds, @ThresholdType = @ThresholdType; /* Remove rows which match your thresholds based on Loginname, and/or hostname and/or program name but do not exceed the duration threshold set */ SET @DELSQLStmtWhere = N''''; SET @DELSQLStmt = N'' /* Statement generated by procedure: [Inspector].[LongRunningTransactionsUpdate] */ DELETE FROM [Inspector].[LongRunningTransactions] WHERE [Servername] = @Servername AND [transaction_begin_time] >= DATEADD(SECOND,-@DurationInSeconds,[Log_Date]) AND [session_id] IS NOT NULL''; IF (@Loginname IS NOT NULL) BEGIN SET @DELSQLStmtWhere += N'' AND [login_name] = @Loginname''; END IF (@Hostname IS NOT NULL) BEGIN SET @DELSQLStmtWhere += N'' AND [host_name] = @Hostname''; END IF (@Programname IS NOT NULL) BEGIN SET @DELSQLStmtWhere += N'' AND [program_name] = @Programname''; END SET @DELSQLStmt += @DELSQLStmtWhere; EXEC sp_executesql @DELSQLStmt, N''@Servername NVARCHAR(128), @Loginname NVARCHAR(128), @Hostname NVARCHAR(128), @Programname NVARCHAR(128), @DurationInSeconds INT'', @Servername = @Servername, @Loginname = @Loginname, @Hostname = @Hostname, @Programname = @Programname, @DurationInSeconds = @DurationInSeconds; IF(@Debug = 1) BEGIN RAISERROR(''%s'',0,0,@DELSQLStmt) WITH NOWAIT; END FETCH NEXT FROM Threshold_cur INTO @Loginname, @Hostname, @Programname, @DurationInSeconds END CLOSE Threshold_cur DEALLOCATE Threshold_cur; /* Update ThresholdType for the remaining rows */ UPDATE [Inspector].[LongRunningTransactions] SET [ThresholdType] = ''Default'' + '' - '' +CAST(@GlobalThreshold AS VARCHAR(10)) +'' Seconds '' +CASE WHEN @GlobalThreshold > 300 THEN ''(''+CAST(CAST(CAST(@GlobalThreshold AS MONEY)/60.00 AS MONEY) AS VARCHAR(10))+'' Minutes)'' ELSE '''' END WHERE [transaction_begin_time] < DATEADD(SECOND,-@GlobalThreshold,[Log_Date]) AND [session_id] IS NOT NULL AND [ThresholdType] IS NULL;'; IF OBJECT_ID('Inspector.LongRunningTransactionsInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[LongRunningTransactionsInsert] AS;'); EXEC sp_executesql N'ALTER PROCEDURE [Inspector].[LongRunningTransactionsInsert] AS BEGIN --Revision date: 08/11/2021 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME; DECLARE @TransactionDurationThreshold INT; DECLARE @GlobalThreshold INT = ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@Servername,''LongRunningTransactions'',''LongRunningTransactionThreshold'') AS INT),300); DECLARE @MinThreshold INT = (SELECT MIN(DurationInSeconds) FROM [Inspector].[LongRunningTransactionsThresholds] WHERE [IsActive] = 1); DECLARE @Now DATETIME = GETDATE(); DECLARE @Retention INT; DECLARE @Loginname NVARCHAR(128); DECLARE @Hostname NVARCHAR(128); DECLARE @Programname NVARCHAR(128); DECLARE @DurationInSeconds INT; DECLARE @SQLStmt NVARCHAR(1000); /* Get the minimum Threshold so that we make sure we capture all the transactions we need, the update that follows will flag which transactions need to be included */ SELECT @TransactionDurationThreshold = MIN(Threshold) FROM (VALUES(@GlobalThreshold),(@MinThreshold)) x (Threshold); IF (@TransactionDurationThreshold IS NULL) BEGIN SET @TransactionDurationThreshold = 300; END SET @Retention = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@Servername, ''LongRunningTransactions'', ''LongRunningTransactionsHistoryRetentionDays'') AS INT), 7)); IF (@Retention IS NULL) BEGIN SET @Retention = 7; END SET @Retention = @Retention*-1; DELETE FROM [Inspector].[LongRunningTransactionsHistory] WHERE Servername = @Servername AND [Log_Date] < DATEADD(DAY,@Retention,GETDATE()); INSERT INTO [Inspector].[LongRunningTransactionsHistory] ([Servername], [Log_Date], [session_id], [transaction_begin_time], [Duration_DDHHMMSS], [TransactionState], [SessionState], [login_name], [host_name], [program_name], [Databasename],[Querytext],[ThresholdType]) SELECT [Servername], [Log_Date], [session_id], [transaction_begin_time], [Duration_DDHHMMSS], [TransactionState], [SessionState], [login_name], [host_name], [program_name], [Databasename],[Querytext],[ThresholdType] FROM [Inspector].[LongRunningTransactions] WHERE [session_id] IS NOT NULL; DELETE FROM [Inspector].[LongRunningTransactions] WHERE Servername = @Servername; --Set a default value of 300 (5 Mins) if NULL IF @TransactionDurationThreshold IS NULL BEGIN SET @TransactionDurationThreshold = 300; END INSERT INTO [Inspector].[LongRunningTransactions] ([Servername], [Log_Date], [session_id], [transaction_begin_time], [Duration_DDHHMMSS], [TransactionState], [SessionState], [login_name], [host_name], [program_name], [Databasename],[Querytext],[ThresholdType]) SELECT @Servername, @Now, SessionTrans.session_id ,ActiveTrans.transaction_begin_time ,RIGHT(''0'' + CONVERT(VARCHAR(4),DATEDIFF(SECOND,ActiveTrans.transaction_begin_time,@Now)%31536000/86400),2) +'':''+RIGHT(''0'' + CONVERT(VARCHAR(2),DATEDIFF(SECOND,ActiveTrans.transaction_begin_time,@Now)%86400/3600),2) +'':''+RIGHT(''0'' + CONVERT(VARCHAR(2),DATEDIFF(SECOND,ActiveTrans.transaction_begin_time,@Now)%3600/60), 2) +'':''+RIGHT(''0'' + CONVERT(VARCHAR(2),(DATEDIFF(SECOND,ActiveTrans.transaction_begin_time,@Now)%60)), 2) AS [Duration_DDHHMMSS] ,CASE ActiveTrans.transaction_state WHEN 0 THEN ''Uninitialised'' WHEN 1 THEN ''Not Started'' WHEN 2 THEN ''Active'' WHEN 3 THEN ''Ended'' WHEN 4 THEN ''Commit Initiated'' WHEN 5 THEN ''Prepared'' WHEN 6 THEN ''Commited'' WHEN 7 THEN ''Rolling Back'' WHEN 8 THEN ''Rolled Back'' ELSE CAST(ActiveTrans.transaction_state AS VARCHAR(20)) END AS TransactionState ,Sessions.status ,Sessions.login_name ,Sessions.host_name ,Sessions.program_name ,DB_NAME(Sessions.database_id) ,[Querytext].[text] ,NULL FROM sys.dm_tran_session_transactions SessionTrans JOIN sys.dm_tran_active_transactions ActiveTrans ON SessionTrans.transaction_id = ActiveTrans.transaction_id JOIN sys.dm_exec_sessions Sessions ON Sessions.session_id = SessionTrans.session_id JOIN sys.dm_exec_connections Connections ON Connections.session_id = Sessions.session_id OUTER APPLY sys.dm_exec_sql_text(Connections.most_recent_sql_handle) aS Querytext WHERE ActiveTrans.transaction_begin_time <= DATEADD(SECOND,-@TransactionDurationThreshold,@Now) ORDER BY ActiveTrans.transaction_begin_time ASC; EXEC [Inspector].[LongRunningTransactionsUpdate] @Servername = @Servername, @Debug = 0; IF NOT EXISTS (SELECT 1 FROM [Inspector].[LongRunningTransactions] WHERE Servername = @Servername) BEGIN INSERT INTO [Inspector].[LongRunningTransactions] ([Servername], [Log_Date], [session_id], [transaction_begin_time], [Duration_DDHHMMSS], [TransactionState], [SessionState], [login_name], [host_name], [program_name], [Databasename]) VALUES(@Servername,@Now,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL) END END'; IF OBJECT_ID('Inspector.UnusedLogshipConfigInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[UnusedLogshipConfigInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[UnusedLogshipConfigInsert] AS BEGIN --Revision date: 30/11/2018 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DELETE FROM [Inspector].[UnusedLogshipConfig] WHERE Servername = @Servername; INSERT INTO [Inspector].[UnusedLogshipConfig] (Servername,Log_Date,Databasename,Databasestate) SELECT @Servername, GETDATE(), LogshippedDBs.secondary_database, CAST( CASE WHEN DBs.state_desc IS NULL THEN ''Database does not exist'' ELSE LOWER(DBs.state_desc) END AS NVARCHAR(128) ) AS Databasestate FROM msdb.dbo.log_shipping_secondary_databases LogshippedDBs LEFT JOIN sys.databases DBs ON LogshippedDBs.secondary_database = DBs.name WHERE DBs.name IS NULL OR DBs.state != 1 --Database does not exist or is not in a restoring state END;'; IF OBJECT_ID('Inspector.DatacollectionsOverdueInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatacollectionsOverdueInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DatacollectionsOverdueInsert] AS --Revision date:13/10/2019 SET NOCOUNT ON; DELETE FROM [Inspector].[DatacollectionsOverdue] WHERE Servername = @@SERVERNAME; --Warn if the Collection has overun the minimum Frequency set for a Module or Modules with the same ModuleConfig_Desc INSERT INTO [Inspector].[DatacollectionsOverdue] ([ExecutionLogID], [Servername], [Log_Date], [ModuleConfig_Desc], [Procname], [DurationInSeconds], [ExecutionDate], [PreviousRunDateTime], [RunNumber], [FrequencyInSeconds], [Variance], [PSCollection]) SELECT [ExecutionLogDetails].[ID] AS ExecutionLogID, [ExecutionLogDetails].[Servername], GETDATE(), [ExecutionLogDetails].[ModuleConfig_Desc], [ExecutionLogDetails].[Procname], CAST([ExecutionLogDetails].[Duration] AS INT) AS DurationInSeconds, [ExecutionLogDetails].[ExecutionDate], [ExecutionLogDetails].[PreviousRunDateTime], [ExecutionLogDetails].[RunNumber], ([ExecutionLogDetails].[ActualFrequency]*60) AS FrequencyInSeconds, ABS(([ExecutionLogDetails].[ActualFrequency]*60) - CAST([ExecutionLogDetails].[Duration] AS INT)) AS Variance, [ExecutionLogDetails].[PSCollection] FROM [Inspector].[ExecutionLogDetails] INNER JOIN (SELECT [ModuleConfig_Desc],MIN(Frequency) AS Frequency FROM [Inspector].[Modules] WHERE [IsActive] = 1 GROUP BY [ModuleConfig_Desc]) AS MinFrequencies ON MinFrequencies.ModuleConfig_Desc = [ExecutionLogDetails].ModuleConfig_Desc WHERE [ExecutionLogDetails].Procname = ''InspectorDataCollection'' AND ([ExecutionLogDetails].[Duration]/60) > MinFrequencies.Frequency;'; IF OBJECT_ID('Inspector.DatacollectionsOverdueReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatacollectionsOverdueReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DatacollectionsOverdueReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS --Revision date: 01/11/2019 BEGIN --Excluded from Warning level control DECLARE @HtmlTableHead VARCHAR(4000); DECLARE @Columnnames VARCHAR(2000); DECLARE @SQLtext NVARCHAR(4000); DECLARE @Frequency INT; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); SET @Frequency = (SELECT ([Frequency]+60) FROM [Inspector].[ModuleConfig] WHERE [ModuleConfig_Desc] = @ModuleConfig); /********************************************************/ --Your query MUST have a case statement that determines which colour to highlight rows --Your query MUST use an INTO clause to populate the temp table so that the column names can be determined for the report --@bgcolor is used the for table highlighting , Warning,Advisory and Info highlighting colours are determined from --the ModuleWarningLevel table and your Case expression And/or Where clause will determine which rows get the highlight --query example: --for collected data reference an Inspector table SELECT CASE WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], [ExecutionLogID], [Servername], [ModuleConfig_Desc], [Procname], [DurationInSeconds], [ExecutionDate], [PreviousRunDateTime], [RunNumber], [FrequencyInSeconds], [Variance], [PSCollection] INTO #InspectorModuleReport FROM [Inspector].[DatacollectionsOverdue] WHERE Servername = @Servername AND [ExecutionDate] > DATEADD(MINUTE,-@Frequency,GETDATE()); --OR ---- for an adhoc query against this server only here is an example --SELECT --CAST( --CASE -- WHEN @WarningLevel = 1 THEN @WarningHighlight -- WHEN @WarningLevel = 2 THEN @AdvisoryHighlight -- WHEN @WarningLevel = 3 THEN @InfoHighlight --END AS VARCHAR(7)) AS [@bgcolor], --DB_NAME(database_id) AS Databasename, --COUNT(*) AS DataFileCount --INTO #InspectorModuleReport --FROM sys.master_files --WHERE database_id = 2 --AND type = 0 --GROUP BY DB_NAME(database_id) --HAVING COUNT(*) < 4; /********************************************************/ SET @Columnnames = ( SELECT STUFF(Columnnames,1,1,'''') FROM ( SELECT '',''+name FROM tempdb.sys.all_columns WHERE [object_id] = OBJECT_ID(N''tempdb.dbo.#InspectorModuleReport'') AND name != N''@bgcolor'' ORDER BY column_id ASC FOR XML PATH('''') ) as g (Columnnames) ); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Data collections overun schedules in the past (''+CAST(@Frequency AS VARCHAR(10))+'' mins)'', --Title for the HTML table, you can use a string here instead such as ''My table title here'' if you want to @TableHeaderColour, @Columnnames) ); SET @SQLtext = N'' SELECT @HtmlOutput = (SELECT '' +''[@bgcolor],'' +REPLACE(@Columnnames,'','','' AS ''''td'''','''''''',+ '') + '' AS ''''td'''','''''''''' +'' FROM #InspectorModuleReport FOR XML PATH(''''tr''''),Elements);'' --Add an ORDER BY if required EXEC sp_executesql @SQLtext,N''@HtmlOutput VARCHAR(MAX) OUTPUT'',@HtmlOutput = @HtmlOutput OUTPUT; --Optional --If in the above query you populate the table with something like ''No issues present'' then you probably do not want that to --show when @Noclutter mode is on --IF (@NoClutter = 1) --BEGIN -- IF(@HtmlOutput LIKE ''%%'') -- BEGIN -- SET @HtmlOutput = NULL; -- END --END --If there is data for the HTML table then build the HTML table IF (@HtmlOutput IS NOT NULL) BEGIN SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


''; END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @SQLtext AS ''@SQLtext'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.ExecutionLogInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[ExecutionLogInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[ExecutionLogInsert] ( @RunDatetime DATETIME, @Servername NVARCHAR(128), @ModuleConfigDesc VARCHAR(20), @Procname NVARCHAR(128), @Frequency SMALLINT = NULL, @ErrorMessage NVARCHAR(128) = NULL, @Duration MONEY, @PSCollection BIT ) AS --Revision Date: 13/05/2021 INSERT INTO [Inspector].[ExecutionLog] (ExecutionDate,Servername,ModuleConfig_Desc,Procname,Frequency,Duration,PSCollection,ErrorMessage) VALUES(@RunDatetime,@Servername,@ModuleConfigDesc,@Procname,@Frequency,@Duration,@PSCollection,@ErrorMessage); '; IF OBJECT_ID('Inspector.ResetHtmlColors') IS NULL EXEC('CREATE PROCEDURE [Inspector].[ResetHtmlColors] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[ResetHtmlColors] AS BEGIN UPDATE [Inspector].[ModuleWarnings] SET [HighlightHtmlColor] = CASE WHEN [WarningLevel] IS NULL THEN NULL WHEN [WarningLevel] = 1 THEN ''#fc5858'' WHEN [WarningLevel] = 2 THEN ''#FAFCA4'' WHEN [WarningLevel] = 3 THEN ''#FEFFFF'' END, [GradientLeftHtmlColor] = CASE WHEN [WarningLevel] IS NULL THEN NULL WHEN [WarningLevel] IN (1,2,3) THEN ''#000000'' END, [GradientRightHtmlColor] = CASE WHEN [WarningLevel] IS NULL THEN NULL WHEN [WarningLevel] = 1 THEN ''#fc5858'' WHEN [WarningLevel] = 2 THEN ''#FAFCA4'' WHEN [WarningLevel] = 3 THEN ''#FEFFFF'' END END'; IF OBJECT_ID('Inspector.DriveCapacityHistory') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DriveCapacityHistory] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DriveCapacityHistory] ( @Servername NVARCHAR(128), @Drive VARCHAR(20) ) AS BEGIN --Revision date 25/03/2019 SELECT Servername, Log_Date, Drive, Capacity_GB, AvailableSpace_GB, DATEDIFF(DAY,LEAD(Log_Date,1,Log_Date) OVER(Partition by Drive ORDER BY Log_Date DESC),Log_Date) AS DaysSinceCapacityChange, Capacity_GB-LEAD(Capacity_GB,1,Capacity_GB) OVER(Partition by Drive ORDER BY Log_Date DESC) AS CapacityChange FROM ( SELECT Servername, Log_Date, Drive, Capacity_GB, AvailableSpace_GB, ROW_NUMBER() OVER(Partition by Capacity_GB,Drive ORDER BY Log_Date DESC) as CapacityChange FROM [Inspector].[DriveSpace] WHERE Drive = @Drive AND Servername = @Servername ) CapacityChanges WHERE CapacityChange = 1 ORDER BY Log_Date DESC END'; IF OBJECT_ID('Inspector.SuppressAdHocDatabase') IS NULL EXEC('CREATE PROCEDURE [Inspector].[SuppressAdHocDatabase] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[SuppressAdHocDatabase] ( @Databasename NVARCHAR(128), @Servername NVARCHAR(128) ) AS BEGIN --Revision date: 05/04/2019 SET NOCOUNT ON; UPDATE [Inspector].[ADHocDatabaseSupression] SET [Suppress] = 1 WHERE [Databasename] = @Databasename AND [Servername] = @Servername; END'; IF OBJECT_ID('Inspector.SuppressAGDatabase') IS NULL EXEC('CREATE PROCEDURE [Inspector].[SuppressAGDatabase] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[SuppressAGDatabase] ( @Databasename NVARCHAR(128), @Servername NVARCHAR(128) ) AS BEGIN --Revision date: 05/04/2019 SET NOCOUNT ON; UPDATE [Inspector].[AGDatabases] SET [Is_AG] = 0 WHERE [Databasename] = @Databasename AND [Servername] = @Servername; END'; IF OBJECT_ID('Inspector.DatabaseGrowthFilenameSync') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatabaseGrowthFilenameSync] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DatabaseGrowthFilenameSync] AS BEGIN --Revision date: 01/05/2019 SET NOCOUNT ON; UPDATE Growths SET [Drive] = [ServerDrives].[Drive] --SELECT --[Growths].GrowthID, --[Growths].[Database_name], --[Growths].[FileName], --[DatabaseFileSizes].[Filename], --[ServerDrives].[Drive], --[Growths].[Drive] FROM [Inspector].[DatabaseFileSizeHistory] Growths INNER JOIN [Inspector].[DatabaseFileSizes] ON [DatabaseFileSizes].Servername = [Growths].Servername AND [DatabaseFileSizes].[Database_name] = [Growths].[Database_name] AND [DatabaseFileSizes].Database_id = [Growths].Database_id AND [DatabaseFileSizes].[Filename] LIKE ''%''+[Growths].[FileName] INNER JOIN (SELECT [Servername],[Drive] FROM [Inspector].[DriveSpace] GROUP BY [Servername],[Drive] ) [ServerDrives] ON [DatabaseFileSizes].Servername = [ServerDrives].[Servername] AND [DatabaseFileSizes].[Filename] LIKE [ServerDrives].[Drive]+''%'' WHERE [Growths].[Drive] IS NULL; UPDATE [Inspector].[Settings] SET [Value] = NULL WHERE [Description] = ''InspectorUpgradeFilenameSync''; END'; IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[TempDBInsert]') AND type in (N'P', N'PC')) BEGIN EXEC dbo.sp_executesql @statement = N'CREATE PROCEDURE [Inspector].[TempDBInsert] AS' END EXEC sp_executesql N'ALTER PROCEDURE [Inspector].[TempDBInsert] AS BEGIN SET NOCOUNT ON; DECLARE @SessionID INT; DECLARE @TransactionStart DATETIME; DECLARE @DurationMins DECIMAL(18,2); DECLARE @TempDBPercentUsed DECIMAL(5,2); SET @TempDBPercentUsed = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@@SERVERNAME, ''TempDB'', ''TempDBPercentUsed'') AS DECIMAL(5,2)), 75.00)); IF (@TempDBPercentUsed IS NULL) BEGIN SET @TempDBPercentUsed = 75.00; END /* we need to remove old records based on retention per server not just the global retention */ DELETE tdb FROM [Inspector].[CurrentServers] cs INNER JOIN [Inspector].[TempDB] tdb ON cs.Servername = tdb.Servername WHERE [Log_Date] < DATEADD(DAY,ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](cs.[Servername], ''TempDB'', ''TempDBDataRetentionDays'') AS INT), 7)*-1,GETDATE()) AND cs.[IsActive] = 1; /* oldest transaction */ SELECT @SessionID = SessionTrans.session_id, @TransactionStart = MIN(ActiveTrans.transaction_begin_time) FROM tempdb.sys.dm_tran_session_transactions SessionTrans JOIN tempdb.sys.dm_tran_active_transactions ActiveTrans ON SessionTrans.transaction_id = ActiveTrans.transaction_id JOIN tempdb.sys.dm_exec_sessions ExecSessions ON ExecSessions.session_id = SessionTrans.session_id JOIN tempdb.sys.dm_exec_connections Connections ON Connections.session_id = ExecSessions.session_id GROUP BY SessionTrans.session_id; /* Calculate the duraion in mins for the oldest transaction */ SET @DurationMins = CAST(DATEDIFF(SECOND,MIN(@TransactionStart),GETDATE())/60.00 AS DECIMAL(18,2)); /* TempDB File utilisation */ INSERT INTO [Inspector].[TempDB] ([Servername],[Log_Date],[DatabaseFilename], [Reserved_MB], [Unallocated_MB], [Internal_object_reserved_MB], [User_object_reserved_MB], [Version_store_reserved_MB], [UsedPct], [OldestTransactionSessionId], [OldestTransactionDurationMins], [TransactionStartTime]) SELECT [Servername], [Log_Date], [DatabaseFilename], [Reserved_MB], [Unallocated_MB], [Internal_object_reserved_MB], [User_object_reserved_MB], [Version_store_reserved_MB], [UsedPct], [OldestTransactionSessionId], [OldestTransactionDurationMins], [TransactionStartTime] FROM ( SELECT @@SERVERNAME AS Servername, GETDATE() AS Log_Date, master_files.name AS DatabaseFilename, CAST((unallocated_extent_page_count+version_store_reserved_page_count+user_object_reserved_page_count+internal_object_reserved_page_count+mixed_extent_page_count)*8/1024.00 AS DECIMAL(18,2)) AS Reserved_MB, CAST(unallocated_extent_page_count*8/1024.00 AS DECIMAL(18,2)) AS Unallocated_MB, CAST((internal_object_reserved_page_count*8)/1024.00 AS DECIMAL(18,2)) AS Internal_object_reserved_MB, CAST((user_object_reserved_page_count*8)/1024.00 AS DECIMAL(18,2)) AS User_object_reserved_MB, CAST(version_store_reserved_page_count*8/1024.00 AS DECIMAL(18,2)) AS Version_store_reserved_MB, CAST( ( ( CAST((internal_object_reserved_page_count*8)/1024.00 AS DECIMAL(18,2))+ CAST((user_object_reserved_page_count*8)/1024.00 AS DECIMAL(18,2))+ CAST(version_store_reserved_page_count*8/1024.00 AS DECIMAL(18,2)) ) /CAST((unallocated_extent_page_count+version_store_reserved_page_count+user_object_reserved_page_count+internal_object_reserved_page_count+mixed_extent_page_count)*8/1024.00 AS DECIMAL(18,2)) )*100.00 AS DECIMAL(18,2) ) AS UsedPct, @SessionID AS OldestTransactionSessionId, @DurationMins AS OldestTransactionDurationMins, @TransactionStart AS TransactionStartTime FROM tempdb.sys.dm_db_file_space_usage INNER JOIN tempdb.sys.master_files ON dm_db_file_space_usage.[file_id] = master_files.[file_id] AND dm_db_file_space_usage.[database_id] = master_files.[database_id] ) AS TempDBUsage WHERE UsedPct >= @TempDBPercentUsed; END'; IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Inspector].[TempDBReport]') AND type in (N'P', N'PC')) BEGIN EXEC dbo.sp_executesql @statement = N'CREATE PROCEDURE [Inspector].[TempDBReport] AS' END EXEC sp_executesql N'ALTER PROCEDURE [Inspector].[TempDBReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 14/06/2021 DECLARE @TempDBPercentUsed DECIMAL(5,2); DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @AgentJobOwnerExclusions VARCHAR(255); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; DECLARE @MonitorHourStart INT; DECLARE @MonitorHourEnd INT; SET @MonitorHourStart = (SELECT [MonitorHourStart] FROM [Inspector].[MonitorHours] WHERE [Servername] = @Servername AND [Modulename] = @Modulename); SET @MonitorHourEnd = (SELECT [MonitorHourEnd] FROM [Inspector].[MonitorHours] WHERE [Servername] = @Servername AND [Modulename] = @Modulename); SET @TempDBPercentUsed = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@@SERVERNAME, ''TempDB'', ''TempDBPercentUsed'') AS DECIMAL(5,2)), 75.00)); SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); IF @MonitorHourStart IS NULL BEGIN SET @MonitorHourStart = 0 END; IF @MonitorHourEnd IS NULL BEGIN SET @MonitorHourEnd = 23 END; --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''TempDB file Usage above ''+CAST(@TempDBPercentUsed AS VARCHAR(10))+''%'' +'' in the last '' +CAST(ABS(@ReportFrequency) AS VARCHAR(6)) +''mins between the hours of '' +CAST(@MonitorHourStart AS VARCHAR(10)) +'' and '' +CAST(@MonitorHourEnd AS VARCHAR(10)), @TableHeaderColour, ''Servername,Log_Date,DatabaseFilename,Reserved_MB,Unallocated_MB,Internal_object_reserved_MB,User_object_reserved_MB,Version_store_reserved_MB,UsedPct,OldestTransactionSessionId,OldestTransactionDurationMins,TransactionStartTime'' ) ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], [Servername] AS ''td'','''',+ CONVERT(VARCHAR(17),[Log_Date],113) AS ''td'','''',+ [DatabaseFilename] AS ''td'','''',+ [Reserved_MB] AS ''td'','''',+ [Unallocated_MB] AS ''td'','''',+ [Internal_object_reserved_MB] AS ''td'','''',+ [User_object_reserved_MB] AS ''td'','''',+ [Version_store_reserved_MB] AS ''td'','''',+ [UsedPct] AS ''td'','''',+ ISNULL(CAST([OldestTransactionSessionId] AS VARCHAR(10)),''N/A'') AS ''td'','''',+ ISNULL(CAST([OldestTransactionDurationMins] AS VARCHAR(10)),''N/A'') AS ''td'','''',+ ISNULL(CONVERT(VARCHAR(24),[TransactionStartTime],113),''N/A'') AS ''td'','''' FROM [Inspector].[TempDB] WHERE Servername = @Servername AND Log_Date >= DATEADD(MINUTE,@ReportFrequency,GETDATE()) AND [DateHour] BETWEEN @MonitorHourStart AND @MonitorHourEnd FOR XML PATH(''tr''),ELEMENTS); END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''',+ ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.PSGetColumns') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSGetColumns] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSGetColumns] ( @Tablename NVARCHAR(128) ) AS BEGIN SET NOCOUNT ON; --Revision date: 23/09/2019 SELECT CAST(STUFF(Columnname,1,1,'''') AS VARCHAR(4000)) AS Columnnames FROM ( SELECT '',''+QUOTENAME(columns.name) FROM sys.tables INNER JOIN sys.columns ON tables.object_id = columns.object_id WHERE tables.name = @Tablename AND [schema_id] = SCHEMA_ID(N''Inspector'') AND [is_computed] = 0 AND [is_ms_shipped] = 0 ORDER BY tables.name ASC,columns.column_id ASC FOR XML PATH('''') ) AS ColumnList (Columnname) END'; IF OBJECT_ID('Inspector.PSGetInspectorBuild') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSGetInspectorBuild] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSGetInspectorBuild] AS BEGIN --Revision date: 14/09/2018 SELECT @@SERVERNAME AS Servername, CAST([Value] AS DECIMAL(4,2)) AS Build FROM [Inspector].[Settings] WHERE [Description] = ''InspectorBuild'' END'; IF OBJECT_ID('Inspector.PSGetConfig') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSGetConfig] AS;') EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSGetConfig] ( @Servername NVARCHAR(128), @ModuleConfig VARCHAR(20) = NULL, @PSExecModules BIT = 0 ) AS BEGIN --Revision date: 01/02/2020 --TableAction: 1 delete, 2 delete with retention, 3 Stage/merge --InsertAction: 1 ALL, 2 Todays'' data only, 3 Frequency based DECLARE @DriveSpaceRetentionPeriodInDays VARCHAR(6) = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@@SERVERNAME, ''DriveSpace'', ''DriveSpaceRetentionPeriodInDays'') AS INT), 90)); IF EXISTS (SELECT 1 FROM [Inspector].[CurrentServers] WHERE [Servername] = @Servername) BEGIN SELECT DISTINCT [PSConfig].Servername, [PSConfig].ModuleConfig_Desc, [PSConfig].Modulename, [PSConfig].Procedurename, [PSConfig].Tablename, [PSConfig].StageTablename, [PSConfig].StageProcname, [PSConfig].TableAction, [PSConfig].InsertAction, [PSConfig].RetentionInDays, (SELECT [Frequency] FROM [Inspector].[Modules] WHERE [Modules].[ModuleConfig_Desc] = [PSConfig].[ModuleConfig_Desc] AND [Modules].[Modulename] = [PSConfig].[Modulename]) AS [Frequency] FROM ( SELECT [Servername], COALESCE(@ModuleConfig,[ModuleConfig_Desc], ''Default'') AS [ModuleConfig_Desc] FROM [Inspector].[CurrentServers] WHERE Servername = @Servername AND IsActive = 1 ) AS ActiveServers INNER JOIN [Inspector].[PSConfig] ON [ActiveServers].ModuleConfig_Desc = [PSConfig].[ModuleConfig_Desc] AND [ActiveServers].[Servername] = [PSConfig].[Servername] WHERE [PSConfig].[IsActive] = 1 AND (EXISTS(SELECT 1 FROM [Inspector].[ModuleSchedulesDue] WHERE ([PSConfig].ModuleConfig_Desc = [ModuleSchedulesDue].[ModuleConfig_Desc] AND [PSConfig].[Modulename] = [ModuleSchedulesDue].[Modulename])) OR @PSExecModules = 1) UNION SELECT @Servername, [ActiveServers].[ModuleConfig_Desc], [NonModuleColection].[Module], [NonModuleColection].[Module]+''Insert'' AS Procedurename, CASE WHEN [NonModuleColection].[Module] = ''InstanceVersion'' THEN ''InstanceVersion,InstanceVersionHistory'' ELSE [NonModuleColection].[Module] END AS Tablename, CASE WHEN [NonModuleColection].[Module] = ''InstanceVersion'' THEN ''N/A,PSInstanceVersionHistoryStage'' ELSE NULL END AS StageTablename, CASE WHEN [NonModuleColection].[Module] = ''InstanceVersion'' THEN ''N/A,PSGetInstanceVersionHistoryStage'' ELSE NULL END AS StageProcname, CASE WHEN [NonModuleColection].[Module] = ''InstanceVersion'' THEN ''1,3'' ELSE ''1'' END AS TableAction, CASE WHEN [NonModuleColection].[Module] = ''InstanceVersion'' THEN ''1,1'' ELSE ''2'' END AS InsertAction, NULL AS RetentionInDays, 1 AS [Frequency] FROM ( SELECT [Servername], COALESCE(@ModuleConfig,[ModuleConfig_Desc], ''Default'') AS [ModuleConfig_Desc] FROM [Inspector].[CurrentServers] WHERE Servername = @Servername AND IsActive = 1 ) AS ActiveServers CROSS APPLY(VALUES(''InstanceStart''), (''InstanceVersion'')) AS NonModuleColection([Module]) ORDER BY [Modulename] ASC; END END'; IF OBJECT_ID('Inspector.PSGetServers') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSGetServers] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSGetServers] AS BEGIN --Revision date: 20/01/2020 SELECT [Servername] FROM [Inspector].[CurrentServers] WHERE [IsActive] = 1 ORDER BY CASE WHEN [Servername] = @@SERVERNAME THEN 2 ELSE 1 END END'; IF OBJECT_ID('Inspector.PSGetADHocDatabaseCreationsStage') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSGetADHocDatabaseCreationsStage] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSGetADHocDatabaseCreationsStage] ( @Servername NVARCHAR(128) ) AS BEGIN --Revision date: 14/09/2018 SET NOCOUNT ON; --Delete previous data for server DELETE FROM [Inspector].[ADHocDatabaseCreations] WHERE Servername = @Servername; --Insert new data for recent collection INSERT INTO [Inspector].[ADHocDatabaseCreations] ([Servername], [Log_Date], [Databasename], [Create_Date]) SELECT [Servername], [Log_Date], [Databasename], [Create_Date] FROM [Inspector].[PSADHocDatabaseCreationsStage] Stage WHERE Servername = @Servername AND NOT EXISTS (SELECT 1 FROM [Inspector].[ADHocDatabaseCreations] Base WHERE Base.Servername = Stage.Servername AND Base.Log_Date = Stage.Log_Date AND Base.Databasename = Stage.Databasename) --Insert if not exists INSERT INTO [Inspector].[ADHocDatabaseSupression] (Servername, Log_Date, Databasename, Suppress) SELECT @Servername, GETDATE(), Databasename, 0 FROM [Inspector].[ADHocDatabaseCreations] Creations WHERE Servername = @Servername AND Databasename != ''No Ad hoc database creations present'' AND NOT EXISTS (SELECT Databasename FROM [Inspector].[ADHocDatabaseSupression] SuppressList WHERE SuppressList.Servername = @Servername AND SuppressList.Databasename = Creations.Databasename); END'; IF OBJECT_ID('Inspector.PSGetAGDatabasesStage') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSGetAGDatabasesStage] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSGetAGDatabasesStage] ( @Servername NVARCHAR(128) ) AS BEGIN --Revision date: 05/04/2019 SET NOCOUNT ON; --Insert new data for recent collection INSERT INTO [Inspector].[AGDatabases] ([Servername], [Log_Date], [LastUpdated], [Databasename], [Is_AG], [Is_AGJoined]) SELECT [Servername], [Log_Date], [LastUpdated], [Databasename], [Is_AG], [Is_AGJoined] FROM [Inspector].[PSAGDatabasesStage] Stage WHERE Servername = @Servername AND NOT EXISTS (SELECT 1 FROM [Inspector].[AGDatabases] Base WHERE Base.Servername = Stage.Servername AND Base.Databasename = Stage.Databasename) --Update any changes and set LastUpdated Datetime (Is_AG is not updated , this is controlled within the Central Table) UPDATE Base SET [Is_AGJoined] = Stage.[Is_AGJoined], [LastUpdated] = GETDATE() FROM [Inspector].[PSAGDatabasesStage] Stage INNER JOIN [Inspector].[AGDatabases] Base ON Base.Servername = Stage.Servername AND Base.Databasename = Stage.Databasename WHERE Stage.Servername = @Servername; END'; IF OBJECT_ID('Inspector.PSGetDriveSpaceStage') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSGetDriveSpaceStage] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSGetDriveSpaceStage] ( @Servername NVARCHAR(128) ) AS BEGIN DECLARE @DriveSpaceRetentionPeriodInDays INT = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@@SERVERNAME, ''DriveSpace'', ''DriveSpaceRetentionPeriodInDays'') AS INT), 90)); --Remove old data for the server DELETE FROM [Inspector].[DriveSpace] WHERE Servername = @Servername AND Log_Date < DATEADD(DAY,-@DriveSpaceRetentionPeriodInDays,DATEADD(DAY,1,CAST(GETDATE() AS DATE))) --Insert new data for recent collection INSERT INTO [Inspector].[DriveSpace] ([Servername], [Log_Date], [Drive], [Capacity_GB], [AvailableSpace_GB]) SELECT [Servername], [Log_Date], [Drive], [Capacity_GB], [AvailableSpace_GB] FROM [Inspector].[PSDriveSpaceStage] Stage WHERE Servername = @Servername AND NOT EXISTS (SELECT 1 FROM [Inspector].[DriveSpace] Base WHERE Base.Servername = Stage.Servername AND Base.Log_Date = Stage.Log_Date) END'; IF OBJECT_ID('Inspector.PSGetDatabaseGrowthsStage') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSGetDatabaseGrowthsStage] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSGetDatabaseGrowthsStage] ( @Servername NVARCHAR(128) ) AS BEGIN --Revision date: 08/04/2019 --Update existing records UPDATE Base SET [Database_name] = [PSStage].[Database_name], [OriginalSize_MB] = [PSStage].[OriginalSize_MB], [Type_desc] = [PSStage].[Type_desc], [File_id] = [PSStage].[File_id], [Filename] = [PSStage].[Filename], [PostGrowthSize_MB] = [PSStage].[PostGrowthSize_MB], [GrowthRate] = [PSStage].[GrowthRate], [Is_percent_growth] = [PSStage].[Is_percent_growth], [NextGrowth] = [PSStage].[NextGrowth], [LastUpdated] = [PSStage].[LastUpdated] FROM [Inspector].[PSDatabaseFileSizesStage] PSStage INNER JOIN [Inspector].[DatabaseFileSizes] Base ON PSStage.Database_id = Base.Database_id AND PSStage.[File_id] = Base.[File_id] AND Base.[Servername] = @Servername WHERE PSStage.Servername = @Servername AND ((PSStage.LastUpdated > Base.LastUpdated OR PSStage.LastUpdated IS NOT NULL AND Base.LastUpdated IS NULL) OR [Base].[Filename] != [PSStage].[Filename]) --Insert missing rows in base from stage table INSERT INTO [Inspector].[DatabaseFileSizes] ([Servername], [Database_id], [Database_name], [OriginalDateLogged], [OriginalSize_MB], [Type_desc], [File_id], [Filename], [PostGrowthSize_MB], [GrowthRate], [Is_percent_growth], [NextGrowth], [LastUpdated]) SELECT [Servername], [Database_id], [Database_name], [OriginalDateLogged], [OriginalSize_MB], [Type_desc], [File_id], [Filename], [PostGrowthSize_MB], [GrowthRate], [Is_percent_growth], [NextGrowth], [LastUpdated] FROM [Inspector].[PSDatabaseFileSizesStage] PSStage WHERE PSStage.Servername = @Servername AND NOT EXISTS (SELECT 1 FROM [Inspector].[DatabaseFileSizes] Base WHERE PSStage.Database_id = Base.Database_id AND PSStage.[File_id] = Base.[File_id] AND Base.Servername = @Servername) --Insert growth events INSERT INTO [Inspector].[DatabaseFileSizeHistory] ([Servername], [Database_id], [Database_name], [Log_Date], [Type_Desc], [File_id], [Drive], [FileName], [PreGrowthSize_MB], [GrowthRate_MB], [GrowthIncrements], [PostGrowthSize_MB]) SELECT [Servername], [Database_id], [Database_name], [Log_Date], [Type_Desc], [File_id], [Drive], [FileName], [PreGrowthSize_MB], [GrowthRate_MB], [GrowthIncrements], [PostGrowthSize_MB] FROM [Inspector].[PSDatabaseFileSizeHistoryStage] PSStage WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[DatabaseFileSizeHistory] Base WHERE PSStage.Database_id = Base.Database_id AND PSStage.[File_id] = Base.[File_id] AND Base.Servername = PSStage.Servername AND PSStage.Log_Date = Base.Log_Date) END'; IF OBJECT_ID('Inspector.PSGetInstanceVersionHistoryStage') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSGetInstanceVersionHistoryStage] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSGetInstanceVersionHistoryStage] ( @Servername NVARCHAR(128) ) AS BEGIN --Revision date: 11/01/2019 SET NOCOUNT ON; --Insert new data for recent collection INSERT INTO [Inspector].[InstanceVersionHistory] ([Servername], [Log_Date], [CollectionDatetime], [VersionNo], [Edition]) SELECT [Servername], [Log_Date], [CollectionDatetime], [VersionNo], [Edition] FROM [Inspector].[PSInstanceVersionHistoryStage] Stage WHERE Servername = @Servername AND NOT EXISTS (SELECT 1 FROM [Inspector].[InstanceVersionHistory] Base WHERE Base.Servername = Stage.Servername AND Base.Log_Date = Stage.Log_Date AND CAST(Base.Log_Date AS DATE) = CAST(Stage.Log_Date AS DATE) ); END'; IF OBJECT_ID('Inspector.PSGetAGPrimaryHistoryStage') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSGetAGPrimaryHistoryStage] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSGetAGPrimaryHistoryStage] ( @Servername NVARCHAR(128) ) AS BEGIN --Revision date: 08/05/2019 SET NOCOUNT ON; --Insert growth events INSERT INTO [Inspector].[AGPrimaryHistory] ([Log_Date], [CollectionDateTime], [Servername], [AGname]) SELECT [Log_Date], [CollectionDateTime], [Servername], [AGname] FROM [Inspector].[PSAGPrimaryHistoryStage] PSStage WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[AGPrimaryHistory] Base WHERE PSStage.[AGname] = Base.[AGname] AND Base.[Servername] = PSStage.[Servername] AND PSStage.[CollectionDateTime] = Base.[CollectionDateTime]) END'; IF OBJECT_ID('Inspector.PSHistCleanup') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSHistCleanup] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSHistCleanup] AS BEGIN --Revision date: 07/07/2021 DECLARE @Retention INT = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@@SERVERNAME, ''DriveSpace'', ''DriveSpaceRetentionPeriodInDays'') AS INT), 90)); --Clean up Drivespace table for history older than @Retention in days DELETE FROM [Inspector].[DriveSpace] WHERE Log_Date < DATEADD(DAY,-@Retention,DATEADD(DAY,1,CAST(GETDATE() AS DATE))); --Clean up the history for growths older than @Retention in days DELETE FROM [Inspector].[DatabaseFileSizeHistory] WHERE [Log_Date] < DATEADD(DAY,-@Retention,GETDATE()); END'; IF OBJECT_ID('Inspector.PopulatePSConfig') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PopulatePSConfig] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PopulatePSConfig] AS BEGIN --Revision date: 27/01/2020 --TableAction: 1 delete, 2 delete with retention, 3 Stage/merge --InsertAction: 1 ALL, 2 Todays'' data only DECLARE @DriveSpaceRetentionPeriodInDays VARCHAR(6); SET @DriveSpaceRetentionPeriodInDays = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@@SERVERNAME, ''DriveSpace'', ''DriveSpaceRetentionPeriodInDays'') AS INT), 90)); INSERT INTO [Inspector].[PSConfig] ([Servername], [ModuleConfig_Desc], [Modulename], [Procedurename], [Tablename], [StageTablename], [StageProcname], [TableAction], [InsertAction], [RetentionInDays], [IsActive]) SELECT Servername, ModuleConfig_Desc, Modulename, Procedurename, Tablename, StageTablename, StageProcname, TableAction, InsertAction, RetentionInDays, 1 FROM ( SELECT [ActiveServers].[Servername], [ActiveServers].[ModuleConfig_Desc], [PSEnabledModules].[Modulename], [CollectionProcedurename] AS Procedurename, CASE WHEN [PSEnabledModules].[Modulename] = ''DatabaseGrowths'' THEN ''DatabaseFileSizes,DatabaseFileSizeHistory'' WHEN [PSEnabledModules].[Modulename] = ''ADHocDatabaseCreations'' THEN ''ADHocDatabaseCreations,ADHocDatabaseSupression'' WHEN [PSEnabledModules].[Modulename] = ''AGCheck'' THEN ''AGCheck,AGPrimaryHistory'' WHEN [CollectionProcedurename] IS NULL THEN NULL ELSE [PSEnabledModules].[Modulename] END AS Tablename, CASE WHEN [PSEnabledModules].[Modulename] IN (''AGDatabases'',''DriveSpace'') THEN ''PS''+[PSEnabledModules].[Modulename]+''Stage'' WHEN [PSEnabledModules].[Modulename] = ''ADHocDatabaseCreations'' THEN ''PSADHocDatabaseCreationsStage,PSADHocDatabaseSupressionStage'' WHEN [PSEnabledModules].[Modulename] = ''DatabaseGrowths'' THEN ''PSDatabaseFileSizesStage,PSDatabaseFileSizeHistoryStage'' WHEN [PSEnabledModules].[Modulename] = ''AGCheck'' THEN ''N/A,PSAGPrimaryHistoryStage'' ELSE NULL END AS StageTablename, CASE WHEN [PSEnabledModules].[Modulename] IN (''AGDatabases'', ''DriveSpace'', ''DatabaseGrowths'', ''ADHocDatabaseCreations'') THEN ''PSGet''+[PSEnabledModules].[Modulename]+''Stage'' WHEN [PSEnabledModules].[Modulename] = ''AGCheck'' THEN ''N/A,PSGetAGPrimaryHistoryStage'' ELSE NULL END AS StageProcname, CASE WHEN [PSEnabledModules].[Modulename] IN (''AGDatabases'',''DriveSpace'') THEN ''3'' WHEN [PSEnabledModules].[Modulename] IN (''ADHocDatabaseCreations'',''DatabaseGrowths'') THEN ''3,3'' WHEN [PSEnabledModules].[Modulename] = ''AGCheck'' THEN ''1,3'' ELSE ''1'' END AS TableAction, --1 delete, 2 delete with retention, 3 Stage/merge CASE WHEN [PSEnabledModules].[Modulename] IN (''AGDatabases'',''BackupSizesByDay'') THEN ''1'' WHEN [PSEnabledModules].[Modulename] IN (''ADHocDatabaseCreations'',''AGCheck'') THEN ''1,1'' WHEN [PSEnabledModules].[Modulename] = ''DatabaseGrowths'' THEN ''1,2'' ELSE ''2'' END AS InsertAction, --1 ALL, 2 Todays'' data only CASE WHEN [PSEnabledModules].[Modulename] = (''DatabaseGrowths'') THEN @DriveSpaceRetentionPeriodInDays+'',''+@DriveSpaceRetentionPeriodInDays WHEN [PSEnabledModules].[Modulename] = (''DriveSpace'') THEN @DriveSpaceRetentionPeriodInDays ELSE NULL END AS RetentionInDays FROM ( SELECT [Servername], ISNULL([ModuleConfig_Desc], ''Default'') AS [ModuleConfig_Desc] FROM [Inspector].[CurrentServers] --WHERE Servername = @Servername WHERE IsActive = 1 ) AS ActiveServers INNER JOIN [Inspector].[PSEnabledModules] ON [ActiveServers].ModuleConfig_Desc = [PSEnabledModules].[ModuleConfig_Desc] ) AS PSConfigList WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[PSConfig] WHERE [PSConfig].[Servername] = [PSConfigList].[Servername] AND [PSConfig].[ModuleConfig_Desc] = [PSConfigList].[ModuleConfig_Desc] AND [PSConfig].[Modulename] = [PSConfigList].[Modulename]); END'; IF OBJECT_ID('Inspector.PSGetSettingsTables') IS NULL EXEC('CREATE PROCEDURE [Inspector].[PSGetSettingsTables] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[PSGetSettingsTables] ( @SortOrder BIT, --0 FOR ORDER BY TableOrder ASC , 1 FOR ORDER BY TableOrder DESC @PSCollection BIT = 0 --If its a powershell collection ensure that the WarningLevel table is populated ) AS --Revision date: 24/11/2020 --Config for Powershell collection use only --TruncateTable - 0 Delete contents, 1 Truncate table --ReseedTable - 0 do not reseed identity, 1 reseed identity BEGIN IF @SortOrder IS NULL BEGIN SET @SortOrder = 0; END IF @PSCollection IS NULL BEGIN SET @PSCollection = 0; END IF @PSCollection = 1 BEGIN EXEC [Inspector].[PopulateModuleWarningLevel]; END IF @SortOrder = 0 BEGIN SELECT Tablename,TruncateTable,ReseedTable FROM (VALUES (1,''Settings'',1,1), (2,''CurrentServers'',0,0), --(3,''EmailRecipients'',0,0), (4,''EmailConfig'',0,0), (5,''CatalogueModules'',0,0), (6,''ModuleWarningLevel'',0,0), (7,''AGCheckConfig'',1,0), (8,''ServerSettingThresholds'',1,0) ) SettingsTables(TableOrder,Tablename,TruncateTable,ReseedTable) ORDER BY TableOrder ASC; END IF @SortOrder = 1 BEGIN SELECT Tablename,TruncateTable,ReseedTable FROM (VALUES (1,''Settings'',1,1), (2,''CurrentServers'',0,0), --(3,''EmailRecipients'',0,0), (4,''EmailConfig'',0,0), (5,''CatalogueModules'',0,0), (6,''ModuleWarningLevel'',0,0), (7,''AGCheckConfig'',1,0), (8,''ServerSettingThresholds'',1,0) ) SettingsTables(TableOrder,Tablename,TruncateTable,ReseedTable) ORDER BY TableOrder DESC; END END'; IF OBJECT_ID('Inspector.CatalogueMissingLogins') IS NULL EXEC('CREATE PROCEDURE [Inspector].[CatalogueMissingLogins] AS;'); --Catalogue reporting procs EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[CatalogueMissingLogins] ( @Servername NVARCHAR(128), @TableHeaderColour VARCHAR(7) = NULL, @WarningLevelFontColour VARCHAR(7) = NULL, @HtmlOutput VARCHAR(MAX) OUTPUT, @ModuleConfig VARCHAR(20), @PSCollection BIT ) AS --Revision date: 09/05/2019 BEGIN SET NOCOUNT ON; DECLARE @PreReqsEnabled INT DECLARE @ReportStart DATETIME = GETDATE(); DECLARE @Duration MONEY --Ensure that all modules required for this check are enabled EXEC sp_executesql N''SELECT @EnabledCount = COUNT([Active]) FROM [Catalogue].[ConfigModules] WHERE ModuleName IN (''''Availability Groups'''',''''Databases'''',''''Logins'''',''''Users'''') AND [Active] = 1;'', N''@EnabledCount INT OUTPUT'',@EnabledCount = @PreReqsEnabled OUTPUT IF @PreReqsEnabled < 4 BEGIN RAISERROR(''PreRequisites for the Catalogue Missing Logins module are not enabled in the Catalogue'',0,0) WITH NOWAIT; RETURN; END IF @TableHeaderColour IS NULL BEGIN SET @TableHeaderColour = ''#E6E6FA'' END; IF OBJECT_ID(''tempdb.dbo.#ServerLogins'') IS NOT NULL DROP TABLE #ServerLogins; CREATE TABLE #ServerLogins ( AGName NVARCHAR(128), LoginName NVARCHAR(128), ServerName NVARCHAR(128) ); EXEC sp_executesql N''INSERT INTO #ServerLogins (AGName,LoginName,ServerName) SELECT DISTINCT AGs.AGName, Logins.LoginName, AGs2.ServerName FROM (SELECT DISTINCT AGName FROM Catalogue.AvailabilityGroups WHERE ServerName = @Servername) AGList INNER JOIN Catalogue.AvailabilityGroups AGs ON AGList.AGName = AGs.AGName INNER JOIN Catalogue.Logins Logins ON AGs.ServerName = Logins.ServerName INNER JOIN Catalogue.AvailabilityGroups AGs2 ON AGs.AGName = AGs2.AGName WHERE NOT EXISTS (SELECT 1 FROM Catalogue.AvailabilityGroups AGs3 JOIN Catalogue.Logins Logins3 ON AGs3.ServerName = Logins3.ServerName WHERE AGs3.AGName = AGs.AGName AND AGs3.ServerName = AGs2.ServerName AND Logins3.LoginName = Logins.LoginName) AND Logins.LoginName NOT IN (SELECT [LoginName] FROM [Inspector].[CatalogueSIDExclusions] WHERE [AGs].[AGName] = [CatalogueSIDExclusions].[AGName]) AND Logins.LoginName != ''''sa'''' AND AGs.LastRecorded >= DATEADD(DAY,-1,GETDATE()) AND Logins.LastRecorded >= DATEADD(DAY,-1,GETDATE()) AND AGs2.LastRecorded >= DATEADD(DAY,-1,GETDATE()) AND EXISTS (SELECT 1 FROM [Catalogue].[ExecutionLog] WHERE [ExecutionDate] >= DATEADD(DAY,-1,GETDATE())) SET @HtmlOutput = ( SELECT @WarningLevelFontColour AS [@bgcolor], ServerName AS ''''td'''','''''''', + LoginName AS ''''td'''','''''''', + CreateCommand AS ''''td'''','''''''' FROM ( SELECT DISTINCT #ServerLogins.ServerName, Logins.LoginName, CASE WHEN Logins.LoginName LIKE ''''%\%'''' THEN ''''CREATE LOGIN '''' + QUOTENAME(Logins.LoginName) + '''' FROM WINDOWS'''' ELSE ''''CREATE LOGIN '''' + QUOTENAME(Logins.LoginName) + '''' WITH PASSWORD = 0x'''' + CONVERT(VARCHAR(MAX), Logins.PasswordHash, 2) + '''' HASHED, SID = 0x'''' + CONVERT(VARCHAR(MAX), Logins.SID, 2) END AS CreateCommand FROM Catalogue.AvailabilityGroups AGs JOIN Catalogue.Logins Logins ON AGs.ServerName = Logins.ServerName JOIN #ServerLogins ON AGs.AGName = #ServerLogins.AGName AND Logins.LoginName = #ServerLogins.LoginName JOIN Catalogue.Users Users ON Users.MappedLoginName = #ServerLogins.LoginName JOIN Catalogue.Databases Databases ON Users.DBName = Databases.DBName AND AGs.AGName = Databases.AGName WHERE AGs.Role = ''''PRIMARY'''' AND #ServerLogins.ServerName = @Servername AND AGs.LastRecorded >= DATEADD(DAY,-1,GETDATE()) AND Logins.LastRecorded >= DATEADD(DAY,-1,GETDATE()) AND Users.LastRecorded >= DATEADD(DAY,-1,GETDATE()) AND Databases.LastRecorded >= DATEADD(DAY,-1,GETDATE()) ) AS MissingLoginInfo FOR XML PATH(''''tr''''),ELEMENTS);'',N''@Servername NVARCHAR(128), @WarningLevelFontColour VARCHAR(7), @HtmlOutput VARCHAR(MAX) OUTPUT'', @Servername = @Servername, @WarningLevelFontColour = @WarningLevelFontColour, @HtmlOutput = @HtmlOutput OUTPUT; IF @HtmlOutput IS NOT NULL BEGIN SET @HtmlOutput = ''Missing Logins:
''+@HtmlOutput +''
Servername LoginName CreateCommand

Back to Top

''; END SET @Duration = CAST(DATEDIFF(MILLISECOND,@ReportStart,GETDATE()) AS MONEY)/1000; EXEC [Inspector].[ExecutionLogInsert] @RunDatetime = @ReportStart, @Servername = @Servername, @ModuleConfigDesc = @ModuleConfig, @Procname = N''CatalogueMissingLogins'', @Duration = @Duration, @PSCollection = @PSCollection; END'; IF OBJECT_ID('Inspector.CatalogueDroppedTables') IS NULL EXEC('CREATE PROCEDURE [Inspector].[CatalogueDroppedTables] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[CatalogueDroppedTables] ( @Servername NVARCHAR(128), @TableHeaderColour VARCHAR(7) = NULL, @WarningLevelFontColour VARCHAR(7) = NULL, @HtmlOutput VARCHAR(MAX) OUTPUT, @ModuleConfig VARCHAR(20), @PSCollection BIT ) AS --Revision date: 16/03/2019 BEGIN DECLARE @LastestExecution DATETIME DECLARE @ReportStart DATETIME = GETDATE(); DECLARE @Duration MONEY IF @TableHeaderColour IS NULL BEGIN SET @TableHeaderColour = ''#E6E6FA'' END; EXEC sp_executesql N''SELECT @LastestExecution = MAX(ExecutionDate) FROM [Catalogue].[ExecutionLog];'', N''@LastestExecution DATETIME OUTPUT'',@LastestExecution = @LastestExecution OUTPUT; EXEC sp_executesql N'' SET @HtmlOutput = ( SELECT @WarningLevelFontColour AS [@bgcolor], CatalogueTables.ServerName AS ''''td'''','''''''', + CatalogueTables.DatabaseName AS ''''td'''','''''''', + CatalogueTables.SchemaName AS ''''td'''','''''''', + CatalogueTables.TableName AS ''''td'''','''''''', + CONVERT(VARCHAR(17),CatalogueTables.LastRecorded,113) AS ''''td'''','''''''' FROM [Inspector].[CurrentServers] InspectorServers INNER JOIN [Catalogue].[Tables] CatalogueTables ON CatalogueTables.ServerName = InspectorServers.Servername WHERE CatalogueTables.ServerName = @Servername AND [CatalogueTables].[LastRecorded] >= DATEADD(DAY,-1,GETDATE()) AND [CatalogueTables].[LastRecorded] < @LastestExecution AND [DatabaseName] != ''''tempdb'''' AND NOT EXISTS (SELECT 1 FROM [Catalogue].[Databases] CatalogueDatabases WHERE CatalogueDatabases.ServerName = InspectorServers.Servername AND CatalogueDatabases.DBName= CatalogueTables.DatabaseName AND [CatalogueDatabases].[LastRecorded] < @LastestExecution) FOR XML PATH(''''tr''''),ELEMENTS);'',N''@Servername NVARCHAR(128), @WarningLevelFontColour VARCHAR(7), @LastestExecution DATETIME,@HtmlOutput VARCHAR(MAX) OUTPUT'', @Servername = @Servername, @WarningLevelFontColour = @WarningLevelFontColour,@LastestExecution = @LastestExecution,@HtmlOutput = @HtmlOutput OUTPUT; IF @HtmlOutput IS NOT NULL BEGIN SET @HtmlOutput = ''Tables dropped in the last 24hrs:
''+@HtmlOutput +''
Servername Database name Schema name Table name LastSeenByCatalogue

Back to Top

''; END SET @Duration = CAST(DATEDIFF(MILLISECOND,@ReportStart,GETDATE()) AS MONEY)/1000; EXEC [Inspector].[ExecutionLogInsert] @RunDatetime = @ReportStart, @Servername = @Servername, @ModuleConfigDesc = @ModuleConfig, @Procname = N''CatalogueDroppedTables'', @Duration = @Duration, @PSCollection = @PSCollection; END'; IF OBJECT_ID('Inspector.CatalogueDroppedDatabases') IS NULL EXEC('CREATE PROCEDURE [Inspector].[CatalogueDroppedDatabases] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[CatalogueDroppedDatabases] ( @Servername NVARCHAR(128), @TableHeaderColour VARCHAR(7) = NULL, @WarningLevelFontColour VARCHAR(7) = NULL, @HtmlOutput VARCHAR(MAX) OUTPUT, @ModuleConfig VARCHAR(20), @PSCollection BIT ) AS --Revision date: 16/03/2019 BEGIN DECLARE @LastestExecution DATETIME DECLARE @ReportStart DATETIME = GETDATE(); DECLARE @Duration MONEY IF @TableHeaderColour IS NULL BEGIN SET @TableHeaderColour = ''#E6E6FA'' END; EXEC sp_executesql N''SELECT @LastestExecution = MAX(ExecutionDate) FROM [Catalogue].[ExecutionLog];'', N''@LastestExecution DATETIME OUTPUT'',@LastestExecution = @LastestExecution OUTPUT; EXEC sp_executesql N'' SET @HtmlOutput = ( SELECT @WarningLevelFontColour AS [@bgcolor], CatalogueDatabases.ServerName AS ''''td'''','''''''', + CatalogueDatabases.DBName AS ''''td'''','''''''', + ISNULL(AGName,N''''Not in an AG'''') AS ''''td'''','''''''', + CatalogueDatabases.FilePaths AS ''''td'''','''''''', + DATEDIFF(DAY,FirstRecorded,LastRecorded) AS ''''td'''','''''''', + CONVERT(VARCHAR(17),CatalogueDatabases.LastRecorded,113) AS ''''td'''','''''''' FROM [Catalogue].[Databases] CatalogueDatabases INNER JOIN [Inspector].[CurrentServers] InspectorServers ON CatalogueDatabases.ServerName = InspectorServers.Servername WHERE CatalogueDatabases.ServerName = @Servername AND [CatalogueDatabases].[LastRecorded] >= DATEADD(DAY,-1,GETDATE()) AND [CatalogueDatabases].[LastRecorded] < @LastestExecution FOR XML PATH(''''tr''''),ELEMENTS);'',N''@Servername NVARCHAR(128), @WarningLevelFontColour VARCHAR(7), @LastestExecution DATETIME,@HtmlOutput VARCHAR(MAX) OUTPUT'', @Servername = @Servername, @WarningLevelFontColour = @WarningLevelFontColour,@LastestExecution = @LastestExecution,@HtmlOutput = @HtmlOutput OUTPUT; IF @HtmlOutput IS NOT NULL BEGIN SET @HtmlOutput = ''Databases dropped in the last 24hrs:
''+@HtmlOutput +''
Servername Database name AG name File paths DaysSeenByCatalogue LastSeenByCatalogue

Back to Top

''; END SET @Duration = CAST(DATEDIFF(MILLISECOND,@ReportStart,GETDATE()) AS MONEY)/1000; EXEC [Inspector].[ExecutionLogInsert] @RunDatetime = @ReportStart, @Servername = @Servername, @ModuleConfigDesc = @ModuleConfig, @Procname = N''CatalogueDroppedDatabases'', @Duration = @Duration, @PSCollection = @PSCollection; END'; IF OBJECT_ID('Inspector.ADHocDatabaseCreationsReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[ADHocDatabaseCreationsReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[ADHocDatabaseCreationsReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Potential Ad hoc database creations in the last 7 days'', @TableHeaderColour, ''Database name,Create date,Suppress database'') ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], Databasename AS ''td'','''', + CONVERT(VARCHAR(17),Create_Date,113) AS ''td'','''', + ''EXEC [''+DB_NAME()+''].[Inspector].[SuppressAdHocDatabase] @Databasename = ''''''+Databasename+'''''', @Servername = ''''''+@Servername+'''''';'' AS ''td'','''' FROM [Inspector].[ADHocDatabaseCreations] WHERE Servername = @Servername AND Databasename != ''No Ad hoc database creations present'' AND Databasename NOT IN ( SELECT Databasename FROM [Inspector].[ADHocDatabaseSupression] Suppressed WHERE Servername = @Servername AND Suppressed.Suppress = 1) ORDER BY Create_Date ASC FOR XML PATH(''tr''),ELEMENTS); IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], ''No Ad hoc database creations present'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No Ad hoc database creations present%'') BEGIN SET @HtmlOutput = NULL; END END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.AGCheckReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[AGCheckReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[AGCheckReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHeadAG VARCHAR(2000); DECLARE @HtmlTableHeadFailover VARCHAR(2000); DECLARE @FailoverCheckHTML VARCHAR(MAX); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHeadAG = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername,@Modulename, @ServerSpecific, ''Availability Group Health Check'', @TableHeaderColour, ''Server name,AG name,State,Replica Server Name,Replica Role,Failover Ready,Suspended,Suspend Reason,Failover Ready Threshold'') ); SET @HtmlTableHeadFailover = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''New Primary servers in the last 24 hours'', @TableHeaderColour, ''Previously checked,Last checked,AG name,Primary Replica'') ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = ( SELECT CASE WHEN @WarningLevel IS NULL AND (([AGCheck].[State] != ''HEALTHY'' AND [AGCheck].[State] != ''N/A'' ) OR ([FailoverReadyCount] < ISNULL([FailoverReadyNodeCount],2)) AND [AGCheck].[ReplicaServername] = @Servername AND [AGCheck].[ReplicaRole] = N''PRIMARY'') THEN @WarningHighlight WHEN @WarningLevel = 1 AND (([AGCheck].[State] != ''HEALTHY'' AND [AGCheck].[State] != ''N/A'') OR ([FailoverReadyCount] < ISNULL([FailoverReadyNodeCount],2)) AND [AGCheck].[ReplicaServername] = @Servername AND [AGCheck].[ReplicaRole] = N''PRIMARY'') THEN @WarningHighlight WHEN @WarningLevel = 2 AND (([AGCheck].[State] != ''HEALTHY'' AND [AGCheck].[State] != ''N/A'') OR ([FailoverReadyCount] < ISNULL([FailoverReadyNodeCount],2)) AND [AGCheck].[ReplicaServername] = @Servername AND [AGCheck].[ReplicaRole] = N''PRIMARY'') THEN @AdvisoryHighlight WHEN @WarningLevel = 3 AND (([AGCheck].[State] != ''HEALTHY'' AND [AGCheck].[State] != ''N/A'') OR ([FailoverReadyCount] < ISNULL([FailoverReadyNodeCount],2)) AND [AGCheck].[ReplicaServername] = @Servername AND [AGCheck].[ReplicaRole] = N''PRIMARY'') THEN @InfoHighlight ELSE ''#FFFFFF'' END AS [@bgcolor], [AGCheck].Servername AS ''td'','''', + [AGCheck].[AGname] AS ''td'','''', + [AGCheck].[State] AS ''td'','''', + ISNULL([AGCheck].[ReplicaServername],''N/A'') AS ''td'','''', + ISNULL([AGCheck].[ReplicaRole],''N/A'') AS ''td'','''', + CASE WHEN [AGCheck].[FailoverReady] = 1 THEN ''Y'' WHEN [AGCheck].[FailoverReady] = 0 THEN ''N'' ELSE ''N/A'' END AS ''td'','''', + CASE WHEN [AGCheck].[Suspended] = 1 THEN ''Y'' WHEN [AGCheck].[Suspended] = 0 THEN ''N'' ELSE ''N/A'' END AS ''td'','''', + ISNULL([AGCheck].[SuspendReason],''N/A'') AS ''td'','''', + ISNULL([FailoverReadyNodeCount],2) AS ''td'','''' FROM [Inspector].[AGCheck] LEFT JOIN (SELECT AGname, COUNT(AGname) AS FailoverReadyCount FROM [Inspector].[AGCheck] WHERE [FailoverReady] = 1 AND [Servername] = @Servername GROUP BY AGname) AS FailoverReadyCounts ON FailoverReadyCounts.AGname = [AGCheck].AGname LEFT JOIN (SELECT AGname, CASE WHEN [FailoverReadyNodeCount] > 10 THEN [FailoverReadyNodePercentCount] ELSE [FailoverReadyNodeCount] END AS [FailoverReadyNodeCount] FROM [Inspector].[AGCheckConfig]) AS FailoverReadyConfig ON [FailoverReadyConfig].[AGname] = [AGCheck].[AGname] WHERE [AGCheck].[Servername] = @Servername ORDER BY [AGCheck].[AGname] ASC,[AGCheck].[ReplicaServername] ASC FOR XML PATH(''tr''),ELEMENTS); --Failover check added in V1.4 /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @FailoverCheckHTML += (SELECT CASE WHEN @WarningLevel IS NULL THEN @WarningHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight ELSE ''#FFFFFF'' END AS [@bgcolor], CONVERT(VARCHAR(17),[Log_Date],113) AS ''td'','''', + CONVERT(VARCHAR(17),[CollectionDateTime],113) AS ''td'','''', + [AGname] AS ''td'','''', + [Servername] AS ''td'','''' FROM [Inspector].[AGPrimaryHistory] WHERE [Servername] = @Servername AND [CollectionDateTime] >= DATEADD(DAY,-1,GETDATE()) ORDER BY [AGname] ASC, [Servername] ASC FOR XML PATH(''tr''),ELEMENTS); SET @FailoverCheckHTML += ISNULL(@TableTail,'''') + ''


'' END --If @NoClutter is on we do not want to show the table if it has @InfoHighlight against the row/s IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%HADR IS NOT ENABLED ON THIS SERVER OR YOU HAVE NO AVAILABILITY GROUPS%'') BEGIN SET @HtmlOutput = NULL; END END IF (@HtmlOutput IS NOT NULL) BEGIN SET @HtmlOutput = @HtmlTableHeadAG + @HtmlOutput + @TableTail +''


'' + CASE WHEN @FailoverCheckHTML IS NOT NULL THEN ISNULL(@HtmlTableHeadFailover,'''') + ISNULL(@FailoverCheckHTML,'''') ELSE '''' END; END END ELSE BEGIN SET @HtmlOutput = ( SELECT CASE WHEN @WarningLevel IS NULL THEN @WarningHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''', + ''Data collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FROM [Inspector].[AGCheck] WHERE Servername = @Servername ORDER BY AGname ASC FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; SET @HtmlOutput = @HtmlTableHeadAG + @HtmlOutput + @TableTail +''


'' END --If @NoClutter is on we do not want to show the table if it has @InfoHighlight against the row/s IF (@HtmlOutput LIKE ''%''+@InfoHighlight+''%'') BEGIN SET @HtmlOutput = NULL; END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHeadAG AS ''@HtmlTableHeadAG'', @HtmlTableHeadFailover AS ''@HtmlTableHeadFailover'', @FailoverCheckHTML AS ''@FailoverCheckHTML'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.AGDatabasesReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[AGDatabasesReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[AGDatabasesReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Databases not in an AG'', @TableHeaderColour, ''Server name,Last Checked,Database name,Suppress database'') ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput =( SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], [Servername] AS ''td'','''', + CONVERT(VARCHAR(17),[LastUpdated],113) AS ''td'','''', + [Databasename] AS ''td'','''', + ''EXEC [''+DB_NAME()+''].[Inspector].[SuppressAGDatabase] @Databasename = ''''''+Databasename+'''''', @Servername = ''''''+@Servername+'''''';'' AS ''td'','''' FROM [Inspector].[AGDatabases] WHERE [Is_AG] = 1 AND [Is_AGJoined] = 0 AND Servername = @Servername ORDER BY [Databasename] ASC FOR XML PATH(''tr''),ELEMENTS); IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput =( SELECT ''#FFFFFF'' AS [@bgcolor], @Servername AS ''td'','''', + ''No Databases marked as AG and not joined'' AS ''td'','''', + ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END --If @NoClutter is on we do not want to show the table if it has @InfoHighlight against the row/s IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No Databases marked as AG and not joined%'') BEGIN SET @HtmlOutput = NULL; END END IF (@HtmlOutput IS NOT NULL) BEGIN SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),Elements); --Mark Collection as out of date SET @CollectionOutOfDate = 1; SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.BackupsCheckReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[BackupsCheckReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[BackupsCheckReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 12/10/2021 DECLARE @FullBackupThreshold INT = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold] (@Servername,@Modulename,''FullBackupThreshold'') AS INT),8)); DECLARE @DiffBackupThreshold INT = (SELECT TRY_CAST([Inspector].[GetServerModuleThreshold] (@Servername,@Modulename,''DiffBackupThreshold'') AS INT)); DECLARE @LogBackupThreshold INT = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold] (@Servername,@Modulename,''LogBackupThreshold'') AS INT),20)); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; DECLARE @MaxFullThreshold INT = (SELECT MAX(FullThreshold) FROM [Inspector].[BackupsCheckThresholds] WHERE [IsActive] = 1); IF (@FullBackupThreshold < @MaxFullThreshold) BEGIN SET @FullBackupThreshold = @MaxFullThreshold; END SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; --Excluded from Warning level control IF OBJECT_ID(''tempdb.dbo.#RawData'') IS NOT NULL DROP TABLE #RawData; CREATE TABLE #RawData ( Log_Date DATETIME, Databasename NVARCHAR(128), LastFull DATETIME, LastDiff DATETIME, LastLog DATETIME, AGname NVARCHAR(128), GroupingMethod NVARCHAR(128), Servername NVARCHAR(128), IsFullRecovery BIT, IsSystemDB BIT, primary_replica NVARCHAR(128), backup_preference NVARCHAR(60) ); IF OBJECT_ID(''tempdb.dbo.#Aggregates'') IS NOT NULL DROP TABLE #Aggregates; CREATE TABLE #Aggregates ( Log_Date DATETIME, Databasename NVARCHAR(128), LastFull DATETIME, LastDiff DATETIME, LastLog DATETIME, AGname NVARCHAR(128), GroupingMethod NVARCHAR(128), IsFullRecovery BIT, IsSystemDB BIT, primary_replica NVARCHAR(128), backup_preference NVARCHAR(60) ); CREATE CLUSTERED INDEX [CIX_RawData] ON #RawData (GroupingMethod,Databasename,IsFullRecovery,IsSystemDB,AGname); IF OBJECT_ID(''tempdb.dbo.#Validations'') IS NOT NULL DROP TABLE #Validations; CREATE TABLE #Validations ( Databasename NVARCHAR(128), AGname NVARCHAR(128), FullState VARCHAR(25), DiffState VARCHAR(25), LogState VARCHAR(25), IsFullRecovery CHAR(1), Serverlist VARCHAR(1000), primary_replica NVARCHAR(128), backup_preference NVARCHAR(60), NamedInstance BIT, Thresholds VARCHAR(128) ); DECLARE @NamedInstance BIT IF @Servername LIKE ''%\%'' BEGIN SET @NamedInstance = 1 END ELSE BEGIN SET @NamedInstance = 0 END; DECLARE @HtmlTableHead VARCHAR(2000); SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] (@Servername,@Modulename,@ServerSpecific,''The following Databases are missing database backups:'',@TableHeaderColour,''Servername,Database name,AG name,Last Full,Last Diff,Last Log,Full Recovery,AG Backup Pref,Preferred Servers,Thresholds'')); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN INSERT INTO #RawData (Log_Date,Databasename,LastFull,LastDiff,LastLog,AGname,GroupingMethod,Servername,IsFullRecovery,IsSystemDB,primary_replica,backup_preference) SELECT Log_Date, LTRIM(RTRIM(BackupSet.Databasename)), --Added trim as Leading and trailing spaces can cause misreporting [FULL] AS LastFull, [DIFF] AS LastDiff, [LOG] AS LastLog, BackupSet.AGname, CASE WHEN BackupSet.AGname = ''Not in an AG'' THEN Servername ELSE BackupSet.AGname END AS GroupingMethod, Servername, BackupSet.IsFullRecovery, BackupSet.IsSystemDB, BackupSet.primary_replica, BackupSet.backup_preference FROM [Inspector].[BackupsCheck] BackupSet WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[BackupsCheckExcludes] WHERE [Servername] = [BackupSet].[Servername] AND [Databasename] = [BackupSet].[Databasename] AND ([SuppressUntil] IS NULL OR [SuppressUntil] >= GETDATE()) ); INSERT INTO #Aggregates (Log_Date,Databasename,LastFull,LastDiff,LastLog,AGname,GroupingMethod,IsFullRecovery,IsSystemDB,primary_replica,backup_preference) SELECT MAX(Log_Date), RawData.Databasename, MAX(LastFull) AS LastFull, MAX(LastDiff) AS LastDiff, MAX(LastLog) AS LastLog, AGname, GroupingMethod, IsFullRecovery, IsSystemDB, MAX(primary_replica), UPPER(backup_preference) AS backup_preference FROM #RawData RawData GROUP BY Databasename,AGname,GroupingMethod,IsFullRecovery,IsSystemDB,backup_preference; INSERT INTO #Validations (Databasename,AGname,FullState,DiffState,LogState,IsFullRecovery,Serverlist,primary_replica,backup_preference,NamedInstance,Thresholds) SELECT Aggregates.Databasename, Aggregates.AGname, CASE WHEN Aggregates.[LastFull] = ''19000101'' THEN ''More than ''+CAST(@FullBackupThreshold AS VARCHAR(3))+'' Days Ago'' WHEN (Aggregates.[LastFull] >= ''19000101'' AND Aggregates.[LastFull] < DATEADD(DAY,-ISNULL([Thresholds].[FullThreshold],@FullBackupThreshold),Aggregates.[Log_Date]) OR Aggregates.[LastFull] IS NULL) THEN ISNULL(CONVERT(VARCHAR(17),Aggregates.[LastFull],113),''More than ''+CAST(@FullBackupThreshold AS VARCHAR(3))+'' days ago'') ELSE ''OK'' END AS [FullState], CASE WHEN [Thresholds].[Databasename] IS NOT NULL AND [Thresholds].[DiffThreshold] IS NULL THEN ''N/A'' WHEN @DiffBackupThreshold IS NOT NULL THEN CASE WHEN Aggregates.[LastDiff] = ''19000101'' AND Aggregates.IsSystemDB = 0 THEN ''More than ''+CAST(@FullBackupThreshold AS VARCHAR(3))+'' Days Ago'' WHEN (Aggregates.[LastDiff] >= ''19000101'' AND Aggregates.[LastDiff] < DATEADD(HOUR,-ISNULL([Thresholds].[DiffThreshold],@DiffBackupThreshold),Aggregates.[Log_Date]) OR Aggregates.[LastDiff] IS NULL) AND Aggregates.IsSystemDB = 0 THEN ISNULL(CONVERT(VARCHAR(17),Aggregates.[LastDiff],113),''More than ''+CAST(@DiffBackupThreshold AS VARCHAR(3))+'' Hours ago'') WHEN Aggregates.IsSystemDB = 1 THEN ''N/A'' ELSE ''OK'' END ELSE ''N/A'' END AS [DiffState], CASE WHEN Aggregates.[LastLog] = ''19000101'' AND Aggregates.IsSystemDB = 0 AND Aggregates.IsFullRecovery = 1 THEN ''More than ''+CAST(@FullBackupThreshold AS VARCHAR(3))+'' Days Ago'' WHEN ((Aggregates.[LastLog] >= ''19000101'' AND Aggregates.[LastLog] < DATEADD(MINUTE,-ISNULL([Thresholds].[LogThreshold],@LogBackupThreshold),Aggregates.[Log_Date]) OR Aggregates.[LastLog] IS NULL) AND Aggregates.IsSystemDB = 0 AND (Aggregates.IsFullRecovery = 1 OR CAST(Aggregates.IsFullRecovery AS VARCHAR(3)) = ''N/A'')) THEN ISNULL(CONVERT(VARCHAR(17),[LastLog] ,113),''More than ''+CAST(@LogBackupThreshold AS VARCHAR(3))+'' Minutes ago'') WHEN Aggregates.IsFullRecovery = 0 OR Aggregates.IsSystemDB = 1 THEN ''N/A'' ELSE ''OK'' END AS [LogState], CASE Aggregates.IsFullRecovery WHEN 1 THEN ''Y'' ELSE ''N'' END AS IsFullRecovery, STUFF(Serverlist.Serverlist,1,1,'''') AS Serverlist, Aggregates.primary_replica, Aggregates.backup_preference, CASE WHEN Aggregates.primary_replica LIKE ''%\%'' THEN 1 ELSE 0 END, ''Full: ''+CAST(ISNULL([FullThreshold],@FullBackupThreshold) AS VARCHAR(20))+'' Day(s) , ''+ ''Diff: ''+ISNULL(CAST((CASE WHEN [Thresholds].[Databasename] IS NOT NULL THEN [DiffThreshold] ELSE @DiffBackupThreshold END) AS VARCHAR(20))+'' Hour(s) , '','' Ignoring , '')+ ''Log: ''+CAST(ISNULL([LogThreshold],@LogBackupThreshold) AS VARCHAR(20))+'' Minute(s)'' AS Thresholds FROM #Aggregates Aggregates LEFT JOIN (SELECT [Servername], [Databasename], [FullThreshold], [DiffThreshold], [LogThreshold] FROM [Inspector].[BackupsCheckThresholds] WHERE [IsActive] = 1) Thresholds ON Aggregates.[primary_replica] = Thresholds.[Servername] AND Aggregates.[Databasename] = Thresholds.[Databasename] CROSS APPLY (SELECT CASE WHEN backup_preference IN (''PRIMARY'',''NON AG'') THEN '', '' + primary_replica ELSE '', '' + Servername END FROM #RawData RawData WHERE Aggregates.GroupingMethod = RawData.GroupingMethod AND Aggregates.Databasename = RawData.Databasename AND Aggregates.IsFullRecovery = RawData.IsFullRecovery AND Aggregates.IsSystemDB = RawData.IsSystemDB AND Aggregates.AGname = RawData.AGname ORDER BY 1 ASC FOR XML PATH('''') ) AS Serverlist (Serverlist); SET @HtmlOutput = ( SELECT @WarningHighlight AS [@bgcolor], Servername AS ''td'','''', + Databasename AS ''td'','''', + AGname AS ''td'','''', + FullState AS ''td'','''', + DiffState AS ''td'','''', + LogState AS ''td'','''', + IsFullRecovery AS ''td'','''', + backup_preference AS ''td'','''', + Serverlist AS ''td'','''', + Thresholds AS ''td'','''' FROM ( SELECT @Servername AS Servername, Databasename, AGname, FullState, DiffState, LogState, IsFullRecovery, CASE WHEN backup_preference = ''PRIMARY'' THEN ''Primary only'' WHEN backup_preference = ''SECONDARY'' THEN ''Prefer secondary'' WHEN backup_preference = ''SECONDARY_ONLY'' THEN ''Secondary only'' WHEN backup_preference = ''NONE'' THEN ''Any replica'' WHEN backup_preference = ''NON AG'' THEN ''N/A'' ELSE backup_preference END AS backup_preference, CASE --Ensure only the relevant server names are being shown in the Server list WHEN ([FullState] != ''OK'' OR [DiffState] != ''OK'' AND [DiffState] != ''N/A'') AND [LogState] = ''OK'' THEN primary_replica WHEN backup_preference = ''SECONDARY_ONLY'' THEN REPLACE(REPLACE(Serverlist,'', ''+@Servername,''''),@Servername+'', '','''') ELSE Serverlist END AS Serverlist, Thresholds FROM #Validations WHERE ([FullState] != ''OK'' OR ([DiffState] != ''OK'' AND [DiffState] != ''N/A'') OR ([LogState] != ''OK'' AND [LogState] != ''N/A'')) AND Serverlist like ''%''+@Servername+''%'' AND NamedInstance = @NamedInstance ) AS DerivedValidations WHERE (Serverlist = Servername OR Serverlist LIKE ''%''+Servername+''%'') ORDER BY Databasename ASC FOR XML PATH(''tr''),ELEMENTS); IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput = ( SELECT ''#FFFFFF'' [@bgcolor], @Servername AS ''td'','''', + ''No backup issues present'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS) END --Append html table to the report SET @HtmlOutput = CASE WHEN @HtmlOutput LIKE ''%No backup issues present%'' AND @NoClutter = 1 THEN '''' ELSE ISNULL(@HtmlTableHead, '''') + ISNULL(@HtmlOutput, '''') +''

Global Thresholds:
Last FULL backup older than ''+CAST(@FullBackupThreshold AS VARCHAR(3))+'' Day/s
''+ CASE WHEN @DiffBackupThreshold IS NOT NULL THEN ''Last DIFF backup older than ''+ CAST(@DiffBackupThreshold AS VARCHAR(3))+'' Hour/s
'' ELSE ''DIFF backups excluded from check
'' END + ''Last Log backup older than ''+CAST(@LogBackupThreshold AS VARCHAR(3))+'' Minute/s
Databases Excluded for this server: ''+(SELECT CAST(COUNT(Servername) AS VARCHAR(6)) FROM [Inspector].[BackupsCheckExcludes] WHERE Servername = @Servername AND ([SuppressUntil] IS NULL OR [SuppressUntil] >= GETDATE()))+''

'' + ISNULL(REPLACE(@TableTail,'''',''''),'''') +''


'' END; --IF (@HtmlOutput IS NOT NULL) --BEGIN --SET @HtmlOutput = -- @HtmlTableHead -- + @HtmlOutput -- + @TableTail -- +''


'' --END END ELSE BEGIN SET @HtmlOutput = ( SELECT @WarningHighlight [@bgcolor], @Servername AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS) --Mark Collection as out of date SET @CollectionOutOfDate = 1; SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.BackupSizesByDayReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[BackupSizesByDayReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[BackupSizesByDayReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] (@Servername,@Modulename,@ServerSpecific,''Backup Sizes by Day for server:'',@TableHeaderColour,''Day Of Week,Total Backup Size GB'')); --Excluded from Warning level control /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], [DayOfWeek] AS ''td'','''', + [TotalBackupSize_GB] AS ''td'','''' FROM ( SELECT [DayOfWeek], [CastedDate], CAST(SUM(((TotalSizeInBytes)/1024)/1024)/1024 AS DECIMAL (10,1)) AS [TotalBackupSize_GB] FROM [Inspector].[BackupSizesByDay] WHERE Servername = @Servername AND Log_Date IS NOT NULL GROUP BY [DayOfWeek],[CastedDate] ) BackupSizesByDay ORDER BY CastedDate ASC FOR XML PATH(''tr''),ELEMENTS); IF (@HtmlOutput IS NOT NULL) BEGIN SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END END ELSE BEGIN IF EXISTS (SELECT Log_Date FROM [Inspector].[BackupSizesByDay] WHERE Servername = @Servername AND Log_Date IS NULL) BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], ''No Backups for the past 7 days'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], ''Data collection out of Date'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.DatabaseFilesReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatabaseFilesReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DatabaseFilesReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Data or Log files on incorrect drives'', @TableHeaderColour, ''Server name,Database name,File type,File path'' ) ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], Servername AS ''td'','''', + Databasename AS ''td'','''', + FileType AS ''td'','''', + FilePath AS ''td'','''' FROM [Inspector].[DatabaseFiles] WHERE Servername = @Servername AND Databasename != ''No Database File issues present'' FOR XML PATH(''tr''),ELEMENTS); IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], @Servername AS ''td'','''', + ''No Database File issues present'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END --If @NoClutter is on we do not want to show the table if it has @InfoHighlight against the row/s IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No Database File issues present%'') BEGIN SET @HtmlOutput = NULL; END END IF (@HtmlOutput IS NOT NULL) BEGIN SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.DatabaseGrowthsReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatabaseGrowthsReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DatabaseGrowthsReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 12/09/2019 --Excluded from Warning level control DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @DatabaseGrowthsAllowedPerDay INT = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold] (@Servername,@Modulename,''DatabaseGrowthsAllowedPerDay'') AS INT),1)); DECLARE @MAXDatabaseGrowthsAllowedPerDay INT = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold] (@Servername,@Modulename,''MAXDatabaseGrowthsAllowedPerDay'') AS INT),10)); SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''The following Database files have grown more than ''+CAST(@DatabaseGrowthsAllowedPerDay AS VARCHAR(5))+'' time(s) in the past 24hours:'', @TableHeaderColour, ''Server name,Database name,Type desc,File ID,Filename,Pre Growth Size MB,Growth Rate MB,Growth Increments,Post Growth Size MB,Suggested Growth Rate MB,Growth Rate trend (Last 5 days)'' ) ); SELECT @HtmlOutput = (SELECT CASE WHEN [GrowthIncrements] > @DatabaseGrowthsAllowedPerDay AND [GrowthIncrements] < @MAXDatabaseGrowthsAllowedPerDay THEN @AdvisoryHighlight WHEN [GrowthIncrements] > @DatabaseGrowthsAllowedPerDay AND [GrowthIncrements] >= @MAXDatabaseGrowthsAllowedPerDay THEN @WarningHighlight END AS [@bgcolor], [Servername] AS ''td'','''', + [Database_name] AS ''td'','''', + [Type_Desc] AS ''td'','''', + [File_id] AS ''td'','''', + [FileName] AS ''td'','''', + [PreGrowthSize_MB] AS ''td'','''', + [GrowthRate_MB] AS ''td'','''', + [GrowthIncrements] AS ''td'','''', + [PostGrowthSize_MB] AS ''td'','''',+ CASE WHEN [GrowthRate_MB] < 100 THEN 100 -- if current growth rate is less than 100MB then suggest a minimum of 100MB ELSE [GrowthRate_MB] * [GrowthIncrements] END AS ''td'','''',+ [HistoricGrowths].[LastFiveDaysGrowth] AS ''td'','''' FROM [Inspector].[DatabaseFileSizeHistory] LatestGrowths CROSS APPLY (SELECT STUFF((SELECT TOP 5 '', ['' + DATENAME(WEEKDAY,DATEADD(DAY,-1,[HistoricGrowths].[Log_Date])) + '' '' + CAST([GrowthRate_MB]*[GrowthIncrements] AS VARCHAR(10)) +'' MB]'' FROM [Inspector].[DatabaseFileSizeHistory] HistoricGrowths WHERE [LatestGrowths].[Servername] = [HistoricGrowths].[Servername] AND [LatestGrowths].[Database_id] = [HistoricGrowths].[Database_id] AND [LatestGrowths].[File_id] = [HistoricGrowths].[File_id] AND [HistoricGrowths].[Log_Date] >= DATEADD(DAY,-5,CAST(GETDATE() AS DATE)) ORDER BY [HistoricGrowths].[Log_Date] DESC FOR XML PATH('''')),1,1,'''') ) AS [HistoricGrowths](LastFiveDaysGrowth) WHERE [Log_Date] >= DATEADD(HOUR,-24,GETDATE()) AND [GrowthIncrements] > @DatabaseGrowthsAllowedPerDay AND [Type_Desc] = ''ROWS'' ORDER BY [Servername],[Database_name],[File_id] FOR XML PATH(''tr''),Elements); IF (@HtmlOutput IS NOT NULL) BEGIN SET @HtmlOutput = ''



Server [ALL Servers]


'' + @HtmlTableHead + @HtmlOutput + ''

Advisory Highlight - More than ''+CAST(@DatabaseGrowthsAllowedPerDay AS VARCHAR(5))+'' growth event/s in the past 24 hours
Warning Highlight - ''+CAST(@MAXDatabaseGrowthsAllowedPerDay AS VARCHAR(5))+'' or more growth event/s in the past 24 hours

'' + ISNULL(REPLACE(@TableTail,'''',''''),'''') + ''


'' +''


''; END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @DatabaseGrowthsAllowedPerDay AS ''@DatabaseGrowthsAllowedPerDay'', @MAXDatabaseGrowthsAllowedPerDay AS ''@MAXDatabaseGrowthsAllowedPerDay'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.DatabaseOwnershipReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatabaseOwnershipReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DatabaseOwnershipReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @DatabaseOwnerExclusions VARCHAR(255) = (SELECT REPLACE([Value],'' '' ,'''') FROM [Inspector].[Settings] WHERE [Description] = ''DatabaseOwnerExclusions'') DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''The following Databases have an owner that is not ''+ISNULL(@DatabaseOwnerExclusions,''[N/A - No Exclusions Set]''), @TableHeaderColour, ''Server name,AG name,Database name,Owner'') ); SET @DatabaseOwnerExclusions = REPLACE(REPLACE(@DatabaseOwnerExclusions,'' '',''''),'','','', ''); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], [Servername] AS ''td'','''', + [AGname] AS ''td'','''', + [Database_name] AS ''td'','''', + [Owner] AS ''td'','''' FROM [Inspector].[DatabaseOwnership] WHERE [Servername] = @Servername AND [Database_name] != ''No Database Ownership issues present'' ORDER BY [Database_name] FOR XML PATH(''tr''),ELEMENTS); IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''No Database Ownership issues present'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END --If @NoClutter is on we do not want to show the table if it has @InfoHighlight against the row/s IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No Database Ownership issues present%'') BEGIN SET @HtmlOutput = NULL; END END IF (@HtmlOutput IS NOT NULL) BEGIN SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.DatabaseSettingsReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatabaseSettingsReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DatabaseSettingsReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @HtmlOutput = ''''; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Database settings'', @TableHeaderColour, ''Collation,Total'') ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SELECT @HtmlOutput = @HtmlOutput + (SELECT ''#FFFFFF'' AS [@bgcolor], [Description] AS ''td'','''', + Total AS ''td'','''' FROM [Inspector].[DatabaseSettings] WHERE Servername = @Servername AND Setting = ''Collation_name'' ORDER BY Total DESC FOR XML PATH(''tr''),ELEMENTS); SELECT @HtmlOutput = @HtmlOutput + '' Auto Close Total ''; SELECT @HtmlOutput = @HtmlOutput + (SELECT CASE WHEN @WarningLevel IS NULL AND [Description] = ''Enabled'' THEN @AdvisoryHighlight WHEN @WarningLevel = 1 AND [Description] = ''Enabled'' THEN @WarningHighlight WHEN @WarningLevel = 2 AND [Description] = ''Enabled'' THEN @AdvisoryHighlight WHEN @WarningLevel = 3 AND [Description] = ''Enabled'' THEN @InfoHighlight ELSE ''#FFFFFF'' END AS [@bgcolor], [Description] AS ''td'','''', + Total AS ''td'','''' FROM [Inspector].[DatabaseSettings] WHERE Servername = @Servername AND Setting = ''is_auto_close_on'' ORDER BY Total DESC FOR XML PATH(''tr''),ELEMENTS); SELECT @HtmlOutput = @HtmlOutput + '' Auto Shrink Total ''; SELECT @HtmlOutput = @HtmlOutput + (SELECT CASE WHEN @WarningLevel IS NULL AND [Description] = ''Enabled'' THEN @AdvisoryHighlight WHEN @WarningLevel = 1 AND [Description] = ''Enabled'' THEN @WarningHighlight WHEN @WarningLevel = 2 AND [Description] = ''Enabled'' THEN @AdvisoryHighlight WHEN @WarningLevel = 3 AND [Description] = ''Enabled'' THEN @InfoHighlight ELSE ''#FFFFFF'' END AS [@bgcolor], [Description] AS ''td'','''', + Total AS ''td'','''' FROM [Inspector].[DatabaseSettings] WHERE Servername = @Servername AND Setting = ''is_auto_shrink_on'' ORDER BY Total DESC FOR XML PATH(''tr''),ELEMENTS); SELECT @HtmlOutput = @HtmlOutput + '' Auto Update Stats Total ''; SELECT @HtmlOutput = @HtmlOutput + (SELECT CASE WHEN @WarningLevel IS NULL AND [Description] = ''Disabled'' THEN @AdvisoryHighlight WHEN @WarningLevel = 1 AND [Description] = ''Disabled'' THEN @WarningHighlight WHEN @WarningLevel = 2 AND [Description] = ''Disabled'' THEN @AdvisoryHighlight WHEN @WarningLevel = 3 AND [Description] = ''Disabled'' THEN @InfoHighlight ELSE ''#FFFFFF'' END AS [@bgcolor], [Description] AS ''td'','''', + Total AS ''td'','''' FROM [Inspector].[DatabaseSettings] WHERE Servername = @Servername AND Setting = ''is_auto_update_stats_on'' ORDER BY Total DESC FOR XML PATH(''tr''),ELEMENTS); SELECT @HtmlOutput = @HtmlOutput + '' Read Only Total ''; SELECT @HtmlOutput = @HtmlOutput + (SELECT ''#FFFFFF'' AS [@bgcolor], [Description] AS ''td'','''', + Total AS ''td'','''' FROM [Inspector].[DatabaseSettings] WHERE Servername = @Servername AND Setting = ''is_read_only'' ORDER BY Total DESC FOR XML PATH(''tr''),ELEMENTS); SELECT @HtmlOutput = @HtmlOutput + '' User Access Total ''; SELECT @HtmlOutput = @HtmlOutput + (SELECT ''#FFFFFF'' AS [@bgcolor], [Description] AS ''td'','''', + Total AS ''td'','''' FROM [Inspector].[DatabaseSettings] WHERE Servername = @Servername AND Setting = ''user_access_desc'' ORDER BY Total DESC FOR XML PATH(''tr''),ELEMENTS); SELECT @HtmlOutput = @HtmlOutput + '' Compatibility Level Total ''; SELECT @HtmlOutput = @HtmlOutput + (SELECT ''#FFFFFF'' AS [@bgcolor], [Description] AS ''td'','''', + Total AS ''td'','''' FROM [Inspector].[DatabaseSettings] WHERE Servername = @Servername AND Setting = ''compatibility_level'' ORDER BY Total DESC FOR XML PATH(''tr''),ELEMENTS); SELECT @HtmlOutput = @HtmlOutput + '' Recovery Model Total ''; SELECT @HtmlOutput = @HtmlOutput + (SELECT ''#FFFFFF'' AS [@bgcolor], [Description] AS ''td'','''', + Total AS ''td'','''' FROM [Inspector].[DatabaseSettings] WHERE Servername = @Servername AND Setting = ''recovery_model_desc'' ORDER BY Total DESC FOR XML PATH(''tr''),ELEMENTS); END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END IF (@HtmlOutput IS NOT NULL) BEGIN SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.DatabaseStatesReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DatabaseStatesReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DatabaseStatesReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Database count by state'', @TableHeaderColour, ''Server name,Database state,Total,Database names'' ) ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput =( SELECT CASE WHEN DatabaseState IN (''RECOVERY_PENDING'',''SUSPECT'',''EMERGENCY'') THEN @WarningHighlight --Cannot be overidden using Warning levels WHEN @WarningLevel IS NULL AND DatabaseState IN (''Restoring'',''RECOVERING'',''OFFLINE'',''SNAPSHOT (more than 10 days old)'') THEN @AdvisoryHighlight WHEN @WarningLevel = 1 AND DatabaseState IN (''Restoring'',''RECOVERING'',''OFFLINE'',''SNAPSHOT (more than 10 days old)'') THEN @WarningHighlight WHEN @WarningLevel = 2 AND DatabaseState IN (''Restoring'',''RECOVERING'',''OFFLINE'',''SNAPSHOT (more than 10 days old)'') THEN @AdvisoryHighlight WHEN @WarningLevel = 3 AND DatabaseState IN (''Restoring'',''RECOVERING'',''OFFLINE'',''SNAPSHOT (more than 10 days old)'') THEN @InfoHighlight ELSE ''#FFFFFF'' END AS [@bgcolor], Servername AS ''td'','''', + DatabaseState AS ''td'','''', + Total AS ''td'','''', + DatabaseNames AS ''td'','''' FROM [Inspector].[DatabaseStates] WHERE Servername = @Servername ORDER BY Total DESC FOR XML PATH(''tr''),ELEMENTS); END ELSE BEGIN SET @HtmlOutput =( SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''', + ''Data collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.DriveSpaceReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[DriveSpaceReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[DriveSpaceReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @DriveLetterExcludes VARCHAR(10), @ServerSpecific BIT, @ModuleConfig VARCHAR(20), @UseMedian BIT, @NoClutter BIT, @TableTail VARCHAR(256), @DriveSpaceTableOnly VARCHAR(MAX) OUTPUT, @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS --Revision date: 27/07/2021 BEGIN SET NOCOUNT ON; /* Excluded from Warning level control */ DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @DaysUntilDriveFullThreshold INT; DECLARE @FreeSpaceRemainingPercent DECIMAL(5,2); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; DECLARE @GlobalMedianCalc BIT; SET @Debug = [Inspector].[GetDebugFlag](@Debug, @ModuleConfig, @Modulename); SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; /*Set columns names for the Html table*/ SET @HtmlTableHead = ( SELECT [Inspector].[GenerateHtmlTableheader] (@Servername, @Modulename, @ServerSpecific, ''Drive space Report:'', /*Title for the HTML table*/ @TableHeaderColour, ''Server name,Drive,Total GB,Available GB,% Free,Est.Daily Growth GB,Days Until Disk Full,Days Recorded,Usage Trend,Usage Trend AVG GB,Calculation method,Thresholds'' ) ); SET @GlobalMedianCalc = ( SELECT TRY_CAST([Value] AS BIT) FROM [Inspector].[Settings] WHERE [Description] = ''UseMedianCalculationForDriveSpaceCalc'' ); SET @DaysUntilDriveFullThreshold = ( SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@Servername, @Modulename, ''DaysUntilDriveFullThreshold'') AS INT), 56) ); SET @FreeSpaceRemainingPercent = ( SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@Servername, @Modulename, ''FreeSpaceRemainingPercent'') AS DECIMAL(5, 2)), 10.00) ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN IF OBJECT_ID(''tempdb.dbo.#TotalDriveEntries'') IS NOT NULL DROP TABLE [#TotalDriveEntries]; CREATE TABLE #TotalDriveEntries ( Servername NVARCHAR(128) NOT NULL, Drive NVARCHAR(128) NULL, TotalEntries INT NOT NULL, MedianCalc BIT NULL, Excluded BIT NOT NULL, DaysRecorded INT NULL, AverageDailyGrowth_GB DECIMAL(10,2) ); INSERT INTO #TotalDriveEntries ([Servername], [Drive], [TotalEntries], [MedianCalc],[Excluded]) SELECT Servername, Drive, COUNT(Drive) AS TotalEntries, CASE WHEN EXISTS (SELECT 1 FROM [Inspector].[DriveSpaceCalc] WHERE [DriveSpaceCalc].Servername = [DriveSpace].Servername AND [DriveSpaceCalc].Drive = [DriveSpace].Drive AND [DriveSpaceCalc].MedianCalc = 1) THEN 1 ELSE ISNULL(@GlobalMedianCalc,0) END, CASE WHEN EXISTS (SELECT 1 FROM [master].[dbo].fn_SplitString(@DriveLetterExcludes, '','') WHERE [StringElement] + '':\'' = [DriveSpace].Drive) THEN 1 ELSE 0 END FROM [Inspector].[DriveSpace] WHERE Servername = @Servername GROUP BY Servername, Drive; UPDATE #TotalDriveEntries SET DaysRecorded = Total FROM ( SELECT DriveByDay.Drive, COUNT(*) AS Total FROM ( SELECT DISTINCT Drive,CAST(Log_Date AS DATE) AS Log_Date FROM Inspector.DriveSpace GROUP BY Drive,CAST(Log_Date AS DATE) ) DriveByDay GROUP BY DriveByDay.Drive ) DriveByDayCounts INNER JOIN #TotalDriveEntries ON DriveByDayCounts.Drive = #TotalDriveEntries.Drive; WITH AverageDailyGrowth AS ( SELECT #TotalDriveEntries.Servername, #TotalDriveEntries.Drive, MedianCalc, CASE WHEN MedianCalc = 1 THEN CAST( ( SELECT TOP 1 PERCENTILE_CONT(0.5) WITHIN GROUP(ORDER BY [UsedSpaceVarianceGB]) OVER(PARTITION BY Drive) FROM Inspector.DriveSpace Median WHERE Median.Drive = #TotalDriveEntries.Drive ) AS DECIMAL(10,2)) ELSE CAST(SUM(([UsedSpaceVarianceGB])/#TotalDriveEntries.TotalEntries) AS DECIMAL(10,2)) END AS AverageDailyGrowth_GB FROM #TotalDriveEntries INNER JOIN Inspector.DriveSpace ON DriveSpace.Drive = #TotalDriveEntries.Drive AND DriveSpace.Servername = #TotalDriveEntries.Servername GROUP BY #TotalDriveEntries.Servername, #TotalDriveEntries.Drive, MedianCalc) UPDATE #TotalDriveEntries SET AverageDailyGrowth_GB = AverageDailyGrowth.AverageDailyGrowth_GB FROM AverageDailyGrowth INNER JOIN #TotalDriveEntries ON #TotalDriveEntries.Servername = AverageDailyGrowth.Servername AND #TotalDriveEntries.Drive = AverageDailyGrowth.Drive; SELECT @HtmlOutput = ( SELECT CASE WHEN AverageDailyGrowth_GB > 0 AND CAST(COALESCE((LastRecordedFreeSpace.AvailableSpace_GB) / NULLIF(AverageDailyGrowth_GB, 0), 0) AS DECIMAL(20, 2)) < ISNULL(Overrides.[DaysUntilDriveFull], @DaysUntilDriveFullThreshold) THEN @WarningHighlight WHEN CAST((LastRecordedFreeSpace.AvailableSpace_GB / LastRecordedFreeSpace.Capacity_GB) * 100 AS DECIMAL(10, 2)) < COALESCE(CAST((Overrides.[MinAvailableSpace_GB] / LastRecordedFreeSpace.Capacity_GB) * 100.00 AS DECIMAL(5, 2)), Overrides.[FreeSpaceRemainingPercent], @FreeSpaceRemainingPercent) AND TotalDriveEntries.Excluded = 0 THEN @AdvisoryHighlight ELSE ''#FFFFFF'' END AS [@bgcolor], TotalDriveEntries.Servername AS ''td'', '''', +TotalDriveEntries.Drive AS ''td'', '''', +LastRecordedFreeSpace.Capacity_GB AS ''td'', '''', +LastRecordedFreeSpace.AvailableSpace_GB AS ''td'', '''', +CAST((LastRecordedFreeSpace.AvailableSpace_GB / LastRecordedFreeSpace.Capacity_GB) * 100 AS DECIMAL(10, 2)) AS ''td'', '''', +ISNULL(TotalDriveEntries.AverageDailyGrowth_GB, 0.00) AS ''td'', '''', +CASE WHEN AverageDailyGrowth_GB <= 0 THEN ''N/A'' ELSE CAST(CAST(COALESCE((LastRecordedFreeSpace.AvailableSpace_GB) / NULLIF(AverageDailyGrowth_GB, 0), 0) AS DECIMAL(20, 2)) AS VARCHAR(10)) END AS ''td'', '''', +TotalDriveEntries.DaysRecorded AS ''td'', '''', +ISNULL(STUFF( ( SELECT '', ['' + DATENAME(WEEKDAY, x.Log_Date) + '' '' + CAST(SUM([UsedSpaceVarianceGB]) AS VARCHAR(10)) + '' GB]'' FROM ( SELECT CAST(SpaceVariation.Log_Date AS DATE) AS Log_Date, [UsedSpaceVarianceGB] FROM Inspector.DriveSpace SpaceVariation WHERE SpaceVariation.Drive = TotalDriveEntries.Drive AND SpaceVariation.Servername = TotalDriveEntries.Servername AND SpaceVariation.Log_Date >= DATEADD(DAY, -5, GETDATE()) ) x GROUP BY x.Log_Date ORDER BY x.Log_Date DESC FOR XML PATH('''') ), 1, 1, ''''), '' No data available'') AS ''td'', '''', +ISNULL(FiveDayTotal.SUMFiveDayTotal, 0.00) AS ''td'', '''', +CASE WHEN TotalDriveEntries.MedianCalc = 1 THEN ''Median'' WHEN TotalDriveEntries.MedianCalc = 0 THEN ''Average'' ELSE ''Average'' END AS ''td'', '''', +''Minimum available space: '' + ISNULL(CAST(CAST(CASE WHEN Overrides.[MinAvailableSpace_GB] IS NOT NULL THEN Overrides.[MinAvailableSpace_GB] ELSE(LastRecordedFreeSpace.Capacity_GB * 1.00) * (CAST(ISNULL(Overrides.[FreeSpaceRemainingPercent], @FreeSpaceRemainingPercent) AS DECIMAL(5, 2)) / 100.00) END AS DECIMAL(10, 2)) AS VARCHAR(128)) + '' GB'', ''Not set'') + '' ('' + ISNULL(CAST(COALESCE(CAST((Overrides.[MinAvailableSpace_GB] / LastRecordedFreeSpace.Capacity_GB) * 100.00 AS DECIMAL(5, 2)), Overrides.[FreeSpaceRemainingPercent], @FreeSpaceRemainingPercent) AS VARCHAR(128)) + ''%'', ''Not set'') + ''), '' + ''Estimated days remaining: '' + ISNULL(CAST(ISNULL(Overrides.[DaysUntilDriveFull], @DaysUntilDriveFullThreshold) AS VARCHAR(128)), ''Not set'') AS ''td'', '''' FROM #TotalDriveEntries TotalDriveEntries LEFT JOIN [Inspector].[DriveSpaceThresholds] Overrides ON TotalDriveEntries.Drive = Overrides.Drive AND TotalDriveEntries.Servername = Overrides.Servername CROSS APPLY ( SELECT TOP 1 [Capacity_GB], [AvailableSpace_GB] FROM [Inspector].[DriveSpace] DriveSpace WHERE DriveSpace.Drive = TotalDriveEntries.Drive AND DriveSpace.Servername = TotalDriveEntries.Servername ORDER BY Log_Date DESC ) AS LastRecordedFreeSpace CROSS APPLY ( SELECT CAST(AVG([UsedSpaceVarianceGB]) AS DECIMAL(20, 2)) AS SUMFiveDayTotal FROM ( SELECT TOP (7200) /* Maximum of 5 days worth of 1 min collections per drive per server */ Drive, UsedSpaceVarianceGB FROM Inspector.DriveSpace FiveDay WHERE FiveDay.Drive = TotalDriveEntries.Drive AND FiveDay.Servername = TotalDriveEntries.Servername AND FiveDay.Log_Date >= DATEADD(DAY, -5, GETDATE()) ORDER BY FiveDay.Log_Date DESC ) AS LastFiveDays ) AS FiveDayTotal WHERE TotalDriveEntries.Servername = @Servername ORDER BY TotalDriveEntries.Drive ASC FOR XML PATH(''tr''),Elements) /* If @NoClutter is on we do not want to show the table if it has @InfoHighlight against the row/s @NoClutter not applicable to DriveSpace module */ IF (@HtmlOutput LIKE ''%''+@InfoHighlight+''%'') BEGIN SET @HtmlOutput = NULL; END IF (@HtmlOutput IS NOT NULL) BEGIN /* @DriveSpaceTableOnly is for internal use only */ SET @DriveSpaceTableOnly = @HtmlOutput; SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


''; END END ELSE BEGIN SET @HtmlOutput = (SELECT @WarningHighlight AS [@bgcolor], @Servername AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),Elements); SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


''; SET @CollectionOutOfDate = 1; END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @HtmlTableHead AS ''@HtmlTableHead'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @DaysUntilDriveFullThreshold AS ''@DaysUntilDriveFullThreshold'', @FreeSpaceRemainingPercent AS ''@FreeSpaceRemainingPercent'', @DriveLetterExcludes AS ''@DriveLetterExcludes'', @ModuleConfig AS ''@ModuleConfig'', @UseMedian AS ''@UseMedian'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.FailedAgentJobsReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[FailedAgentJobsReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[FailedAgentJobsReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Failed Agent Jobs'', @TableHeaderColour, ''Server name,Log Date,Job name,Last Step Failed,Last Failed Date,Last Error'' ) ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @WarningHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], Servername AS ''td'','''', + CONVERT(VARCHAR(17),Log_Date,113) AS ''td'','''', + Jobname AS ''td'','''', + LastStepFailed AS ''td'','''', + CONVERT(VARCHAR(17),LastFailedDate,113) AS ''td'','''',+ LastError + ''...'' AS ''td'','''' FROM [Inspector].[FailedAgentJobs] WHERE Servername = @Servername AND Jobname != ''No Failed Jobs present'' FOR XML PATH(''tr''),ELEMENTS); IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], @Servername AS ''td'','''', + ''N/A'' AS ''td'','''', + ''No Failed Jobs present'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @WarningHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''', + ''N/A'' AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END --Append html table to the report IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No Failed Jobs present%'') BEGIN SET @HtmlOutput = NULL; END END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.GenerateHeaderInfo') IS NULL EXEC('CREATE PROCEDURE [Inspector].[GenerateHeaderInfo] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[GenerateHeaderInfo] ( @Servername NVARCHAR(128), @ModuleConfig VARCHAR(20), @Modulename VARCHAR(50), @ModuleBodyText VARCHAR(MAX), @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @WarningLevelFontColour VARCHAR(7), @CollectionOutOfDate BIT, @NoClutter BIT, @WarningLevel TINYINT, @ServerSpecific BIT, @TableTail VARCHAR(65) OUTPUT, @Importance VARCHAR(6) OUTPUT, @CountWarning INT OUTPUT, @CountAdvisory INT OUTPUT, @AlertHeader VARCHAR(1000) OUTPUT, @AdvisoryHeader VARCHAR(1000) OUTPUT, @InfoHeader VARCHAR(1000) OUTPUT, @Debug BIT = 0 ) AS --Revision date: 13/05/2021 BEGIN DECLARE @HeaderText VARCHAR(100); DECLARE @CountInfo INT = 0; DECLARE @MultiWarningModule BIT = (SELECT 1 FROM [Inspector].[MultiWarningModules] WHERE [Modulename] = @Modulename) --If the module passed in is not server specific then replace @servername with ''ALL_SERVERS'' IF (@ServerSpecific = 0) BEGIN SET @Servername = ''ALL_SERVERS''; END --Remove table tail as a colour key might exist in the table tail (DriveSpace for example) and this will affect header counts counts. IF CHARINDEX('''',@ModuleBodyText) > 0 BEGIN SET @ModuleBodyText = LEFT(@ModuleBodyText,CHARINDEX('''',@ModuleBodyText)-1); END --Set the header text for the module SET @HeaderText = (SELECT [HeaderText] FROM [Inspector].[Modules] WHERE [Modulename] = @Modulename AND [ModuleConfig_Desc] = @ModuleConfig); --Set a default value of the module name if no row was returned from above query IF (@HeaderText IS NULL) BEGIN --Set default header text SET @HeaderText = (SELECT [HeaderText] FROM [Inspector].[DefaultHeaderText] WHERE [Modulename] = @Modulename); IF (@HeaderText IS NULL) --If it is still NULL just set to the Modulename BEGIN SET @HeaderText = @Modulename; END END IF (@WarningLevel > 3) BEGIN SET @WarningLevel = 3; END IF (@WarningLevelFontColour IS NULL) BEGIN SET @WarningLevelFontColour = CASE WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END; END --Multi warning modules need to be treated differently as there are two highlights --Populate Alert header IF (@WarningLevel = 1 OR @MultiWarningModule = 1) BEGIN IF @ModuleBodyText LIKE ''%''+@WarningHighlight+''%'' BEGIN --Multi warning modules need to be overidden as they use two highlights IF (@MultiWarningModule = 1) BEGIN SET @WarningLevelFontColour = @WarningHighlight END SET @CountWarning = (LEN(@ModuleBodyText) - LEN(REPLACE(@ModuleBodyText,@WarningHighlight, '''')))/LEN(@WarningHighlight) SELECT @AlertHeader = CAST('''' AS VARCHAR(200)) + CASE --Add hyperlinks to the respective table and also Anchors for Back to Top for each table WHEN @CollectionOutOfDate = 0 THEN ''''+@Servername+'' - has (''+CAST(@CountWarning AS VARCHAR(5))+'') ''+@HeaderText+''

'' WHEN @CollectionOutOfDate = 1 THEN ''''+@Servername+'' - has (''+CAST(@CountWarning AS VARCHAR(5))+'') ''+@HeaderText+'' (Data collection out of Date)

'' END SET @Importance = ''High''; END END --Populate Advisory header IF (@WarningLevel = 2 OR @MultiWarningModule = 1) BEGIN IF @ModuleBodyText LIKE ''%''+@AdvisoryHighlight+''%'' BEGIN --Multi warning modules need to be overidden as they use two highlights IF (@MultiWarningModule = 1) BEGIN SET @WarningLevelFontColour = @AdvisoryHighlight END SET @CountAdvisory = (LEN(@ModuleBodyText) - LEN(REPLACE(@ModuleBodyText,@AdvisoryHighlight, '''')))/LEN(@AdvisoryHighlight) SELECT @AdvisoryHeader = CAST('''' AS VARCHAR(200)) + CASE --Add hyperlinks to the respective table and also Anchors for Back to Top for each table WHEN @CollectionOutOfDate = 0 THEN ''''+@Servername+'' - has (''+CAST(@CountAdvisory AS VARCHAR(5))+'') ''+@HeaderText+''

'' WHEN @CollectionOutOfDate = 1 THEN ''''+@Servername+'' - has (''+CAST(@CountAdvisory AS VARCHAR(5))+'') ''+@HeaderText+'' (Data collection out of Date)

'' END --If Multi warning is enabled for this module and Importance was set to high in the previous block then do not reset Importance SET @Importance = ( SELECT CASE WHEN (@MultiWarningModule = 1 AND @Importance IN (''High'')) THEN @Importance WHEN (@MultiWarningModule = 1 AND @Importance IS NULL) THEN ''Normal'' WHEN (@MultiWarningModule = 0) THEN ''Normal'' ELSE ''Normal'' END ); END END --Populate Info header IF (@WarningLevel = 3 OR @MultiWarningModule = 1) BEGIN IF @ModuleBodyText LIKE ''%''+@InfoHighlight+''%'' BEGIN --Multi warning modules need to be overidden as they use two highlights IF (@MultiWarningModule = 1) BEGIN SET @WarningLevelFontColour = @InfoHighlight END SET @CountInfo = (LEN(@ModuleBodyText) - LEN(REPLACE(@ModuleBodyText,@InfoHighlight, '''')))/LEN(@InfoHighlight) SELECT @InfoHeader = CAST('''' AS VARCHAR(200)) + CASE --Add hyperlinks to the respective table and also Anchors for Back to Top for each table WHEN @CollectionOutOfDate = 0 THEN ''''+@Servername+'' - has (''+CAST(@CountInfo AS VARCHAR(5))+'') ''+@HeaderText+''

'' WHEN @CollectionOutOfDate = 1 THEN ''''+@Servername+'' - has (''+CAST(@CountInfo AS VARCHAR(5))+'') ''+@HeaderText+'' (Data collection out of Date)

'' END --If Multi warning is enabled for this module and Importance was set to high or Normal in the previous blocks then do not reset Importance SET @Importance = ( SELECT CASE WHEN (@MultiWarningModule = 1 AND @Importance IN (''High'',''Normal'')) THEN @Importance WHEN (@MultiWarningModule = 1 AND @Importance IS NULL) THEN ''Low'' WHEN (@MultiWarningModule = 0) THEN ''Low'' ELSE ''Low'' END ); END END --If no Headers are populated then revert the table tail to the standard back to top IF COALESCE(@AlertHeader,@AdvisoryHeader,@InfoHeader) IS NULL BEGIN SET @TableTail = ''

Back to Top

''; END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @ModuleConfig AS ''@ModuleConfigDetermined'', @Modulename AS ''@Modulename'', @ModuleBodyText AS ''@ReportModuleHtml'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @WarningLevelFontColour AS ''@WarningLevelFontColour'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @NoClutter AS ''@NoClutter'', @WarningLevel AS ''@WarningLevel'', @Importance AS ''@Importance'', @CountWarning AS ''@CountWarning'', @CountAdvisory AS ''@CountAdvisory'', @CountInfo AS ''@CountInfo'', @AlertHeader AS ''@AlertHeader'', @AdvisoryHeader AS ''@AdvisoryHeader'', @InfoHeader AS ''@InfoHeader'', @TableTail AS ''@TableTail''; END END'; SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' CREATE FUNCTION [Inspector].[GenerateHtmlTableheader] ( @Servername NVARCHAR(128), @Modulename VARCHAR(128), @ServerSpecific BIT, @Tableheadermessage VARCHAR(128) = '''', @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @Columnnames VARCHAR(4000) ) RETURNS VARCHAR(MAX) AS BEGIN DECLARE @TableHeader VARCHAR(MAX) IF (@ServerSpecific = 0) BEGIN SET @Servername = N''ALL_SERVERS''; END --Remove space following commas SET @Columnnames = REPLACE(@Columnnames,'', '','',''); SELECT @TableHeader = ''''+@Tableheadermessage+''

''+ (SELECT '''' FROM master.dbo.fn_SplitString(@Columnnames,'','') FOR XML PATH(''''), TYPE) .value(''.'', ''VARCHAR(MAX)''); RETURN(@TableHeader); END ' EXEC(@SQLStatement); SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' CREATE FUNCTION [Inspector].[GetDebugFlag] ( @Debug BIT, @ModuleConfig VARCHAR(20), @Modulename VARCHAR(50) ) RETURNS BIT AS BEGIN --IF @Debug = 1 check that it is enabled for debug in the Modules table - this can get spammy if there is a blanket debug IF (@Debug = 1) BEGIN IF NOT EXISTS (SELECT 1 FROM [Inspector].[Modules] WHERE [ModuleConfig_Desc] = @ModuleConfig AND [Modulename] = @Modulename AND [Debug] = @Debug) BEGIN SET @Debug = 0; END END ELSE --If Debug is active in the Modules table then this will override @Debug = 0 BEGIN IF EXISTS (SELECT 1 FROM [Inspector].[Modules] WHERE [ModuleConfig_Desc] = @ModuleConfig AND [Modulename] = @Modulename AND [Debug] = 1) BEGIN SET @Debug = 1; END END RETURN(@Debug); END ' EXEC(@SQLStatement); SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' CREATE FUNCTION [Inspector].[GetWarningLevel] ( @ModuleConfig VARCHAR(20), @Modulename VARCHAR(50) ) RETURNS TINYINT AS --Revision date: 08/09/2019 BEGIN DECLARE @WarningLevel TINYINT; --Determine warning level for the module SET @WarningLevel = (SELECT [WarningLevel] FROM [Inspector].[Modules] WHERE [ModuleConfig_Desc] = @ModuleConfig AND [Modulename] = @Modulename); RETURN(@WarningLevel); END' EXEC(@SQLStatement); SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' CREATE FUNCTION [Inspector].[GetServerInfo] ( @Servername NVARCHAR(128) ) RETURNS VARCHAR(256) AS BEGIN DECLARE @ServerInfo VARCHAR(256); SELECT @ServerInfo = (SELECT ISNULL(''Machine type: ''+CAST([machine_type] AS VARCHAR(10))+''
'','''')+CHAR(13), ISNULL(''Total RAM: ''+CAST([physical_memory_gb] AS VARCHAR(10))+'' GB
'','''')+CHAR(13), ISNULL(''CPU Count: ''+CAST([cpu_count] AS VARCHAR(10))+''
'','''')+CHAR(13), ISNULL(''Hyperthread Count: ''+CAST([hyperthread_count] AS VARCHAR(10))+''
'','''')+CHAR(13), ISNULL(''Scheduler Count: ''+CAST([scheduler_count] AS VARCHAR(10))+''
'','''')+CHAR(13), ISNULL(''CPU Affinity: ''+CAST([affinity_type_desc] AS VARCHAR(10))+''
'','''')+CHAR(13) FROM [Inspector].[ServerInfo] WHERE Servername = @Servername FOR XML PATH(''''), TYPE).value(''.'', ''VARCHAR(256)''); RETURN(@ServerInfo); END' EXEC(@SQLStatement); IF OBJECT_ID('Inspector.JobOwnerReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[JobOwnerReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[JobOwnerReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @AgentJobOwnerExclusions VARCHAR(255); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @AgentJobOwnerExclusions = (SELECT REPLACE([Value],'' '' ,'''') FROM [Inspector].[Settings] WHERE [Description] = ''AgentJobOwnerExclusions''); SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Agent Jobs where the owner is not ''+ISNULL(REPLACE(REPLACE(@AgentJobOwnerExclusions,'' '',''''),'','','', ''),''[N/A - No Exclusions Set]''), @TableHeaderColour, ''Server name,Job ID,Job name'' ) ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], Servername AS ''td'','''',+ Job_ID AS ''td'','''', + Jobname AS ''td'','''' FROM [Inspector].[JobOwner] WHERE Servername = @Servername AND Jobname != ''No Job Owner issues present'' FOR XML PATH(''tr''),ELEMENTS); IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], @Servername AS ''td'','''',+ ''N/A'' AS ''td'','''', + ''No Job Owner issues present'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''',+ ''N/A'' AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No Job Owner issues present%'') BEGIN SET @HtmlOutput = NULL; END END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.LoginAttemptsReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[LoginAttemptsReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[LoginAttemptsReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Failed Login Attempts'', @TableHeaderColour, ''Server name,Username,Attempts,Last Failed Attempt,Last Error'' ) ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @InfoHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], Servername AS ''td'','''',+ Username AS ''td'','''',+ Attempts AS ''td'','''',+ CONVERT(VARCHAR(17),LastErrorDate,113) AS ''td'','''',+ LastError AS ''td'','''' FROM [Inspector].[LoginAttempts] WHERE Servername = @Servername AND Username != ''No Failed Logins present'' FOR XML PATH(''tr''),ELEMENTS) IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], @Servername AS ''td'','''',+ ''No Failed Logins present'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS) END END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @InfoHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''',+ ''Data Collection out of date'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No Failed Logins present%'') BEGIN SET @HtmlOutput = NULL; END END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.LongRunningTransactionsReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[LongRunningTransactionsReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[LongRunningTransactionsReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 08/11/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @AgentJobOwnerExclusions VARCHAR(255); DECLARE @LongRunningTransactionThreshold VARCHAR(255); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @LongRunningTransactionThreshold = (SELECT CAST([Value] AS INT) FROM [Inspector].[Settings] WHERE [Description] = ''LongRunningTransactionThreshold''); --Default value IF @LongRunningTransactionThreshold IS NULL BEGIN SET @LongRunningTransactionThreshold = 300 END SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Long running transactions'', @TableHeaderColour, ''Server name,Session id,Transaction begin time,Duration (DDHHMMSS),Transaction state,Session state,Login name,Host name,Program name,Database name,ThresholdType'' ) ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], [Servername] AS ''td'','''',+ [session_id] AS ''td'','''',+ CONVERT(VARCHAR(20),[transaction_begin_time],113) AS ''td'','''',+ [Duration_DDHHMMSS] AS ''td'','''',+ [TransactionState] AS ''td'','''',+ [SessionState] AS ''td'','''',+ [login_name] AS ''td'','''',+ [host_name] AS ''td'','''',+ [program_name] AS ''td'','''',+ [Databasename] AS ''td'','''',+ [ThresholdType] AS ''td'','''' FROM [Inspector].[LongRunningTransactions] WHERE Servername = @Servername AND [transaction_begin_time] IS NOT NULL ORDER BY [transaction_begin_time] ASC FOR XML PATH(''tr''),ELEMENTS); IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], [Servername] AS ''td'','''',+ ''No Long running transactions'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''' FROM [Inspector].[LongRunningTransactions] WHERE Servername = @Servername AND [transaction_begin_time] IS NULL ORDER BY [transaction_begin_time] ASC FOR XML PATH(''tr''),ELEMENTS); END END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''',+ ''Data Collection out of date'' AS ''td'','''',+ ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No Long running transactions%'') BEGIN SET @HtmlOutput = NULL; END END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.ModuleReportProcTemplate') IS NULL EXEC('CREATE PROCEDURE [Inspector].[ModuleReportProcTemplate] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[ModuleReportProcTemplate] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS --Revision date: 20/04/2021 BEGIN DECLARE @HtmlTableHead VARCHAR(4000); DECLARE @Columnnames VARCHAR(2000); DECLARE @SQLtext NVARCHAR(4000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); /********************************************************/ --Your query MUST have a case statement that determines which colour to highlight rows --Your query MUST use an INTO clause to populate the temp table so that the column names can be determined for the report --@bgcolor is used the for table highlighting , Warning,Advisory and Info highlighting colours are determined from --the ModuleWarningLevel table and your Case expression And/or Where clause will determine which rows get the highlight --Look for /** OPTIONAL **/ headings throughout the query as you may want to change defaults. --query example: /** REQUIRED **/ --Add your query below --for collected data reference an Inspector table /* SELECT CASE WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], Servername, CONVERT(VARCHAR(17),Log_Date,113) AS Log_Date, Jobname, LastStepFailed, CONVERT(VARCHAR(17),LastFailedDate,113) AS LastFailedDate, LastError + ''...'' INTO #InspectorModuleReport FROM [Inspector].[FailedAgentJobs] WHERE Servername = @Servername AND Jobname != ''No Failed Jobs present''; */ --OR -- For an adhoc query against this server only at report time here is an example /* SELECT CASE WHEN SUM(size) > 16384 THEN @WarningHighlight WHEN SUM(size) BETWEEN 8096 AND 16384 THEN @AdvisoryHighlight WHEN SUM(size) BETWEEN 4096 AND 8096 THEN @InfoHighlight ELSE ''#ffffff'', ''TempDB Data file size'' AS Checkname, SUM(size) AS TotalSize INTO #InspectorModuleReport FROM sys.master_files WHERE database_id = 2 AND type = 0 */ /********************************************************/ --No change required here , this part grabs the column names from the temp table created above SET @Columnnames = ( SELECT STUFF(Columnnames,1,1,'''') FROM ( SELECT '',''+name FROM tempdb.sys.all_columns WHERE [object_id] = OBJECT_ID(N''tempdb.dbo.#InspectorModuleReport'') AND name != N''@bgcolor'' ORDER BY column_id ASC FOR XML PATH('''') ) as g (Columnnames) ); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, @Modulename, --/** OPTIONAL **/ Title for the HTML table, you can use a string here instead such as ''My table title here'' if you want to @TableHeaderColour, @Columnnames) ); SET @SQLtext = N'' SELECT @HtmlOutput = (SELECT '' +''[@bgcolor],'' +REPLACE(@Columnnames,'','','' AS ''''td'''','''''''',+ '') + '' AS ''''td'''','''''''''' +'' FROM #InspectorModuleReport FOR XML PATH(''''tr''''),Elements);'' /** OPTIONAL **/ --Add an ORDER BY if required EXEC sp_executesql @SQLtext,N''@HtmlOutput VARCHAR(MAX) OUTPUT'',@HtmlOutput = @HtmlOutput OUTPUT; --/** OPTIONAL **/ --If in the above query you populate the table with something like ''No issues present'' then you probably do not want that to --show when @Noclutter mode is on IF (@NoClutter = 1) BEGIN IF(@HtmlOutput LIKE ''%%'') BEGIN SET @HtmlOutput = NULL; END END --No Change needed here, this part will put all the report pieces together. --If there is data for the HTML table then build the HTML table IF (@HtmlOutput IS NOT NULL) BEGIN SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


''; END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @SQLtext AS ''@SQLtext'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.ServerSettingsReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[ServerSettingsReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[ServerSettingsReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 21/05/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime](@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Server settings'', @TableHeaderColour, ''Setting name,Value in use,Old value in use,Your config value,Config is active,Change type,Time of change'') ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SELECT @HtmlOutput = ( SELECT CASE WHEN [ChangeType] = N''A change to server configuration was detected'' THEN @WarningHighlight WHEN [ChangeType] = N''A change to server configuration was detected. Server value differs from your config value'' THEN @WarningHighlight WHEN [ChangeType] = N''A change to server configuration was detected. Server value matches your config value'' THEN @InfoHighlight WHEN [ChangeType] = N''No change detected. Server value differs from your config value'' THEN @AdvisoryHighlight WHEN [ChangeType] LIKE ''%Installation default in use'' THEN @AdvisoryHighlight ELSE ''#FFFFFF'' END AS [@bgcolor], [Setting] AS ''td'','''', + ISNULL([ssavalue_in_use],[ServerSettingInUse]) AS ''td'','''', + [old_value_in_use] AS ''td'','''', + ISNULL(CAST([ssaconfig_value_in_use] AS NVARCHAR(10)),[YourConfigValue]) AS ''td'','''', + ISNULL([IsActive],0) AS ''td'','''', + /* ConfigIsActive */ [ChangeType] AS ''td'','''', + CASE WHEN [AuditDate] IS NOT NULL THEN N''Change occurred between ''+ISNULL(CONVERT(NVARCHAR(17),[PrevLastUpdated],113),N''N/A'')+N'' and ''+ISNULL(CONVERT(NVARCHAR(17),[AuditDate],113),N''N/A'') ELSE N''N/A'' END AS ''td'','''' FROM ( SELECT [ss].[Setting], [ss].[value_in_use] AS ServerSettingInUse, [ssc].[value_in_use] AS sscvalue_in_use, [ssa].[value_in_use] AS ssavalue_in_use, ISNULL(CAST([ssc].[value_in_use] AS NVARCHAR(10)),N''N/A'') AS YourConfigValue, CASE /* Check if a aetting has been changed since the last report */ WHEN [AuditDate] IS NOT NULL THEN N''A change to server configuration was detected'' ELSE N''No change detected'' END+ CASE WHEN [SQLDefaultSetting] = 1 THEN N'', Installation default in use'' WHEN ISNULL(ssa.[value_in_use],[ss].[value_in_use]) != ISNULL(ssa.[config_value_in_use],[ssc].[value_in_use]) THEN N''. Server value differs from your config value'' WHEN ISNULL(ssa.[value_in_use],[ss].[value_in_use]) = ISNULL(ssa.[config_value_in_use],[ssc].[value_in_use]) THEN N''. Server value matches your config value'' WHEN [ssc].[value_in_use] IS NULL THEN N'', No config value set in Inspector.ServerSettingsConfig'' ELSE '''' END AS ChangeType, --ISNULL(CONVERT(VARCHAR(20),[ss].[LastUpdated],113),N''N/A'') AS LastUpdated, ssa.[AuditDate], ssa.[PrevLastUpdated], ISNULL(CAST(ssa.[old_value_in_use] AS VARCHAR(10)),N''N/A'') AS old_value_in_use, ssa.[config_value_in_use] AS ssaconfig_value_in_use, ssc.[IsActive] FROM [Inspector].[ServerSettings] ss LEFT JOIN (SELECT [Servername],[Setting],[value_in_use],0 AS SQLDefaultSetting,IsActive FROM [Inspector].[ServerSettingsConfig] WHERE [ServerSettingsConfig].[IsActive] = 1 AND [Servername] = @Servername UNION SELECT [Servername],[Setting],[value_in_use],1,0 AS IsActive FROM [Inspector].[ServerSettings] WHERE [Servername] = @Servername AND ( ([ServerSettings].[Setting] = N''max server memory (MB)'' AND [value_in_use] = 2147483647) OR ([ServerSettings].[Setting] = N''max degree of parallelism'' AND [value_in_use] = 0) ) AND EXISTS (SELECT 1 FROM [Inspector].[ServerSettingsConfig] WHERE [ServerSettingsConfig].[IsActive] = 0 AND [ServerSettingsConfig].[Setting] = [ServerSettings].[Setting] AND [ServerSettingsConfig].[Servername] = [ServerSettings].[Servername] ) ) [ssc] ON [ss].[Setting] = [ssc].[Setting] AND [ss].[Servername] = [ssc].[Servername] LEFT JOIN [Inspector].[ServerSettingsAudit] ssa ON ss.Servername = ssa.Servername AND ss.Setting = ssa.Setting AND [AuditDate] > DATEADD(MINUTE,@ReportFrequency,GETDATE()) WHERE ([ss].Servername = @Servername) ) ServerSettings WHERE ( /* A change has been detected */ ([ChangeType] LIKE ''A change to server configuration%'') OR /* No change detected but the current value in use differs from your config value */ ([ChangeType] = N''No change detected. Server value differs from your config value'' OR [ChangeType] = N''No change detected, Installation default in use'') OR /* You have set warning level 0 (show all) */ (@WarningLevel = 0) ) ORDER BY [AuditDate] DESC, CASE WHEN [ChangeType] = N''A change to server configuration was detected'' THEN 1 WHEN [ChangeType] = N''A change to server configuration was detected. Server value differs from your config value'' THEN 2 WHEN [ChangeType] = N''A change to server configuration was detected. Server value matches your config value'' THEN 3 WHEN [ChangeType] = N''No change detected. Server value differs from your config value'' THEN 5 WHEN [ChangeType] LIKE ''%Installation default in use'' THEN 4 ELSE 6 END ASC FOR XML PATH(''tr''),ELEMENTS); END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @WarningHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.SuspectPagesReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[SuspectPagesReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[SuspectPagesReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Suspect Pages Check'', @TableHeaderColour, ''Server name,Database name,File ID,Page ID,Event type,Error count,Last update'' ) ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SELECT @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL AND [Databasename] IS NOT NULL THEN @WarningHighlight WHEN @WarningLevel = 1 AND [Databasename] IS NOT NULL THEN @WarningHighlight WHEN @WarningLevel = 2 AND [Databasename] IS NOT NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 3 AND [Databasename] IS NOT NULL THEN @InfoHighlight ELSE ''#FFFFFF'' END AS [@bgcolor], [Servername] AS ''td'','''', + ISNULL([Databasename],''No Suspect pages found'') AS ''td'','''', + ISNULL([file_id],''-'') AS ''td'','''', + ISNULL([page_id],''-'') AS ''td'','''', + ISNULL([event_type],''-'') AS ''td'','''', + ISNULL([error_count],''-'') AS ''td'','''', + ISNULL(CONVERT(VARCHAR(17),[last_update_date],113),''-'') AS ''td'','''' FROM [Inspector].[SuspectPages] WHERE Servername = @Servername ORDER BY [last_update_date] ASC FOR XML PATH(''tr''),ELEMENTS); END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @WarningHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),Elements); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No Suspect pages found%'') BEGIN SET @HtmlOutput = NULL; END END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.TopFiveDatabasesReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[TopFiveDatabasesReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[TopFiveDatabasesReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Top 5 Databases by size'', @TableHeaderColour, ''Server name,Database name,Total size(MB)'' ) ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], Servername AS ''td'','''', + Databasename AS ''td'','''', + TotalSize_MB AS ''td'','''' FROM [Inspector].[TopFiveDatabases] WHERE Servername = @Servername FOR XML PATH(''tr''),ELEMENTS); END ELSE BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], @Servername AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.UnusedLogshipConfigReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[UnusedLogshipConfigReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[UnusedLogshipConfigReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 20/04/2021 DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastCollection DATETIME; DECLARE @ReportFrequency INT; SET @LastCollection = [Inspector].[GetLastCollectionDateTime] (@Modulename); EXEC [Inspector].[GetModuleConfigFrequency] @ModuleConfig, @Frequency = @ReportFrequency OUTPUT; SET @ReportFrequency *= -1; SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Unused secondary log shipping config'', @TableHeaderColour, ''Servername,Database name,State'' ) ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= DATEADD(MINUTE,@ReportFrequency,GETDATE())) BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], Servername AS ''td'','''', + Databasename AS ''td'','''', + Databasestate AS ''td'','''' FROM [Inspector].[UnusedLogshipConfig] WHERE Servername = @Servername ORDER BY [Databasename] ASC FOR XML PATH(''tr''),ELEMENTS); IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput = (SELECT ''#FFFFFF'' AS [@bgcolor], @Servername AS ''td'','''', + ''No unused log shipping config present'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); --Mark Collection as out of date SET @CollectionOutOfDate = 1; END IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No unused log shipping config present%'') BEGIN SET @HtmlOutput = NULL; END END SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'Inspector.InspectorReportMaster') AND type in (N'P', N'PC')) BEGIN EXEC dbo.sp_executesql @statement = N'CREATE PROCEDURE [Inspector].[InspectorReportMaster] AS'; END EXEC dbo.sp_executesql @statement = N' ALTER PROCEDURE [Inspector].[InspectorReportMaster] ( @EmailGroup VARCHAR(50) = ''DBA'', @PSCollection BIT = 0, @CachedReportID UNIQUEIDENTIFIER = NULL ) AS --Revision date: 22/05/2020 DECLARE @ModuleConfigDesc VARCHAR(20); DECLARE @ReportWarningsOnly TINYINT; DECLARE @NoClutter BIT; DECLARE @Frequency SMALLINT; DECLARE @Procname NVARCHAR(128) = OBJECT_NAME(@@PROCID); DECLARE @Duration MONEY; DECLARE @ReportStart DATETIME = GETDATE(); DECLARE @EmailProfile NVARCHAR(128); DECLARE @EmailAsAttachment BIT; DECLARE InspectorReportmaster_cur CURSOR LOCAL STATIC FOR SELECT ModuleConfig_Desc,ReportWarningsOnly,NoClutter,Frequency,EmailGroup,EmailProfile,EmailAsAttachment FROM [Inspector].[ReportSchedulesDue] UNION --We want to be sure we are not duplicating rows SELECT ModuleConfig_Desc,ReportWarningsOnly,NoClutter,Frequency,EmailGroup,EmailProfile,EmailAsAttachment FROM [Inspector].[ReportsDueCache] WHERE ID = @CachedReportID OPEN InspectorReportmaster_cur FETCH NEXT FROM InspectorReportmaster_cur INTO @ModuleConfigDesc,@ReportWarningsOnly,@NoClutter,@Frequency,@EmailGroup,@EmailProfile,@EmailAsAttachment WHILE @@FETCH_STATUS = 0 BEGIN RAISERROR(''Executing [Inspector].[SQLUnderCoverInspectorReport] for config ''''%s'''''',0,0,@ModuleConfigDesc) WITH NOWAIT; EXEC sp_executesql N'' EXEC [Inspector].[SQLUnderCoverInspectorReport] @EmailDistributionGroup = @EmailGroup, @EmailProfile = @EmailProfile, @TestMode = 0, @ModuleDesc = @ModuleConfigDesc, @ReportWarningsOnly = @ReportWarningsOnly, @Theme = ''''Dark'''', @NoClutter = @NoClutter, @EmailAsAttachment = @EmailAsAttachment;'', N''@ModuleConfigDesc VARCHAR(20),@ReportWarningsOnly TINYINT,@NoClutter BIT,@EmailGroup VARCHAR(50),@EmailProfile NVARCHAR(128),@EmailAsAttachment BIT'', @ModuleConfigDesc = @ModuleConfigDesc, @ReportWarningsOnly = @ReportWarningsOnly, @NoClutter = @NoClutter, @EmailGroup = @EmailGroup, @EmailProfile = @EmailProfile, @EmailAsAttachment = @EmailAsAttachment; UPDATE [Inspector].[ModuleConfig] SET LastRunDateTime = GETDATE() WHERE ModuleConfig_Desc = @ModuleConfigDesc; IF (@CachedReportID IS NOT NULL) BEGIN DELETE FROM [Inspector].[ReportsDueCache] WHERE ID = @CachedReportID AND ModuleConfig_Desc = @ModuleConfigDesc; END FETCH NEXT FROM InspectorReportmaster_cur INTO @ModuleConfigDesc,@ReportWarningsOnly,@NoClutter,@Frequency,@EmailGroup,@EmailProfile,@EmailAsAttachment END CLOSE InspectorReportmaster_cur DEALLOCATE InspectorReportmaster_cur'; IF OBJECT_ID('Inspector.BackupSpaceInsert') IS NULL EXEC('CREATE PROCEDURE [Inspector].[BackupSpaceInsert] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[BackupSpaceInsert] AS BEGIN SET NOCOUNT ON; --Revision date: 18/12/2019 DECLARE @Servername NVARCHAR(128) = @@SERVERNAME DECLARE @BackupPath NVARCHAR(1000) = (SELECT NULLIF([Value],'''') From [Inspector].[Settings] where [Description] = ''BackupsPath''); DELETE FROM [Inspector].[BackupSpace] WHERE Servername = @Servername; WITH BackupPaths AS ( SELECT CAST(StringElement AS VARCHAR(256)) AS BackupPath FROM master.dbo.fn_SplitString(@BackupPath,'','') ) INSERT INTO [Inspector].[BackupSpace] ([Servername],[Log_Date],[DayOfWeek],[CastedDate],[BackupPath],[TotalSizeInBytes]) SELECT @Servername, GETDATE(), [DayOfWeek] , [CastedDate], BackupPath, [TotalSizeInBytes] FROM ( SELECT DATENAME(WEEKDAY,backup_start_date) AS [DayOfWeek], CAST(backup_start_date AS DATE) AS [CastedDate], CASE WHEN physical_device_name LIKE ''''+BackupPath+''%'' THEN BackupPath ELSE ''Other Path - Not tracked in [Inspector].[Settings]'' END AS BackupPath, SUM(ISNULL(compressed_backup_size,backup_size)) AS [TotalSizeInBytes] FROM msdb.dbo.backupset INNER JOIN msdb.dbo.backupmediafamily ON backupset.media_set_id = backupmediafamily.media_set_id LEFT JOIN BackupPaths ON backupmediafamily.physical_device_name LIKE ''''+BackupPath+''%'' WHERE backup_start_date >= DATEADD(DAY,-7,CAST(GETDATE() AS DATE)) AND backup_start_date < CAST(GETDATE() AS DATE) AND NOT EXISTS (SELECT 1 FROM msdb.dbo.restorehistory WHERE backupset.backup_set_id = restorehistory.backup_set_id) GROUP BY DATENAME(WEEKDAY,backup_start_date), CAST(backup_start_date AS DATE), CASE WHEN physical_device_name LIKE ''''+BackupPath+''%'' THEN BackupPath ELSE ''Other Path - Not tracked in [Inspector].[Settings]'' END ) as BackupSizesbyDay; END'; IF OBJECT_ID('Inspector.BackupSpaceReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[BackupSpaceReport] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[BackupSpaceReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS BEGIN --Revision date: 23/11/2020 --Excluded from Warning level control DECLARE @BackupPathToCheck VARCHAR(256) DECLARE @BackupPaths NVARCHAR(1000) DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @Server NVARCHAR(128); DECLARE @WeekdayOffset INT; SET @HtmlOutput = ''''; SET @WeekdayOffset = (SELECT ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold] (@Servername,@Modulename,''BackupSpaceWeekdayOffset'') AS INT),0)); IF @WeekdayOffset IS NULL BEGIN SET @WeekdayOffset = 0 END; IF @WeekdayOffset > 1 BEGIN SET @WeekdayOffset = 1 END; SET @BackupPaths = (SELECT NULLIF([Value],'''') From [Inspector].[Settings] where [Description] = ''BackupsPath''); SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Backup space information against backup path(s): ''+ISNULL(@BackupPaths,''''), @TableHeaderColour, ''Servername,Backup location,Backup Estimate For ''+CASE WHEN @WeekdayOffset = 1 THEN ''tomorrow'' ELSE ''Tonight'' END+'' GB,Backup Server FreeSpace GB,Backup Server Free Space After Backups GB'') ); DECLARE DriveSpace_cur CURSOR LOCAL STATIC FOR SELECT ISNULL(BackupSpace.Servername,''N\A''), StringElement FROM master.dbo.fn_SplitString(@BackupPaths,'','') Paths LEFT JOIN (SELECT DISTINCT Servername,BackupPath FROM [Inspector].[BackupSpace]) AS BackupSpace ON Paths.StringElement COLLATE DATABASE_DEFAULT = BackupSpace.BackupPath; OPEN DriveSpace_cur FETCH NEXT FROM DriveSpace_cur INTO @Server,@BackupPathToCheck WHILE @@FETCH_STATUS = 0 BEGIN IF (Select [value] FROM sys.configurations WHERE name = ''xp_cmdshell'') = 1 BEGIN DECLARE @ErrorEncountered BIT = 0 DECLARE @ErrorEncounteredText VARCHAR(100) DECLARE @BackupSizeForNextWeekday AS DECIMAL(10,1) DECLARE @BackupSpaceLessStorageSpace AS DECIMAL(10,1) DECLARE @ExtractedInformation VARCHAR(MAX) = '''' DECLARE @FreeSpace_Bytes DECIMAL(18,1); DECLARE @FreeSpace_GB DECIMAL(18,1); DECLARE @BackupPathRaw VARCHAR(256) = @BackupPathToCheck DECLARE @Xpcmd VARCHAR(128) IF OBJECT_ID(''tempdb.dbo.#BackupDriveSpace'') IS NOT NULL DROP TABLE #BackupDriveSpace; CREATE TABLE #BackupDriveSpace ( BytesFree NVARCHAR(MAX) ); IF @BackupPathToCheck LIKE ''%\'' BEGIN SET @BackupPathToCheck = LEFT(@BackupPathToCheck,LEN(@BackupPathToCheck)-1); END SET @Xpcmd = ''DIR\ ''+@BackupPathToCheck; INSERT INTO #BackupDriveSpace (BytesFree) EXEC xp_cmdshell @Xpcmd; IF EXISTS (SELECT TOP 1 BytesFree FROM #BackupDriveSpace WHERE BytesFree IS NOT NULL AND BytesFree NOT IN (''The device is not ready.'', ''The system cannot find the path specified.'',''The network path was not found.'',''The specified path is invalid'',''The filename, directory name, or volume label syntax is incorrect.'') ) BEGIN --Extract the drive information based on the @BackupPathToCheck value (Start the string at this point ignoring any drives letters prior to it) SELECT @ExtractedInformation = @ExtractedInformation + ( SELECT RIGHT(BytesFree,LEN(BytesFree)-CHARINDEX(@BackupPathToCheck,BytesFree)+1) as DriveidentificationSTART FROM (SELECT BytesFree as BytesFree FROM #BackupDriveSpace FOR XML PATH('''') ) IdentifyDriveSpace (BytesFree)) IF (@ExtractedInformation LIKE ''%File Not Found%'') BEGIN SET @ErrorEncountered = 1; SET @ErrorEncounteredText = ''Invalid Backup Path Specified in [Inspector].[Settings]''; END ELSE IF (@ExtractedInformation LIKE ''%Access is denied%'') BEGIN SET @ErrorEncountered = 1; SET @ErrorEncounteredText = ''Access denied to Backup Path Specified in [Inspector].[Settings]''; END ELSE BEGIN SELECT @FreeSpace_Bytes = TRY_CONVERT(BIGINT, REPLACE(RIGHT(LEFT(@ExtractedInformation,CHARINDEX(''bytes free'',@ExtractedInformation)-1), LEN(LEFT(@ExtractedInformation,CHARINDEX(''bytes free'',@ExtractedInformation)-1))- CHARINDEX(''Dir(s)'',LEFT(@ExtractedInformation,CHARINDEX(''bytes free'',@ExtractedInformation)-1))-6),'','','''')); IF @FreeSpace_Bytes IS NULL BEGIN SET @ErrorEncountered = 1; SET @ErrorEncounteredText = ''Unable to determine free space for the Backup Path Specified in [Inspector].[Settings]''; END SET @FreeSpace_GB = ((CAST(@FreeSpace_Bytes AS MONEY)/1024)/1000)/1000; END END ELSE BEGIN SET @ErrorEncountered = 1; SET @ErrorEncounteredText = ''Invalid Backup Path Specified in [Inspector].[Settings]''; END END ELSE BEGIN SET @ErrorEncountered = 1; SET @ErrorEncounteredText = ''xp_cmdshell must be enabled''; END SET @BackupSizeForNextWeekday = (SELECT ISNULL(CAST(SUM(((TotalSizeInBytes)/1024)/1024)/1024 AS DECIMAL (10,1)),0) FROM [Inspector].[BackupSpace] WHERE [DayOfWeek] = DATENAME(WEEKDAY,DATEADD(DAY,@WeekdayOffset,Getdate())) AND [Servername] = @Server AND [BackupPath] = @BackupPathRaw ) IF @BackupSizeForNextWeekday IS NULL BEGIN SET @BackupSizeForNextWeekday = 0 ; END IF @BackupPathToCheck IS NOT NULL BEGIN IF @ErrorEncountered = 0 BEGIN SET @BackupSpaceLessStorageSpace = CAST(@FreeSpace_GB AS DECIMAL(10,1)) - @BackupSizeForNextWeekday SELECT @HtmlOutput = @HtmlOutput + (SELECT CASE WHEN @FreeSpace_GB < (@BackupSizeForNextWeekday + (@BackupSizeForNextWeekday*10) /100) --Warn when the free space on the backup location is less than the estimated size for the next days backups multiplied by 10%. THEN @WarningHighlight ELSE ''#FFFFFF'' END AS [@bgcolor], @Server AS ''td'','''',+ @BackupPathToCheck AS ''td'','''',+ @BackupSizeForNextWeekday AS ''td'','''',+ @FreeSpace_GB AS ''td'','''',+ @BackupSpaceLessStorageSpace AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END IF @ErrorEncountered = 1 BEGIN SELECT @HtmlOutput = @HtmlOutput + (SELECT @WarningHighlight AS [@bgcolor], @Server AS ''td'','''',+ @BackupPathToCheck AS ''td'','''',+ @BackupSizeForNextWeekday AS ''td'','''',+ @ErrorEncounteredText AS ''td'','''',+ ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END END ELSE BEGIN SET @BackupSpaceLessStorageSpace = CAST(@FreeSpace_GB AS DECIMAL(10,1)) - @BackupSizeForNextWeekday; SELECT @HtmlOutput = @HtmlOutput + (SELECT @WarningHighlight AS [@bgcolor], @Server AS ''td'','''',+ @BackupPathToCheck AS ''td'','''',+ @BackupSizeForNextWeekday AS ''td'','''',+ ''BackupPath is Set to NULL, Check Inspector.Settings'' AS ''td'','''',+ ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END FETCH NEXT FROM DriveSpace_cur INTO @Server,@BackupPathToCheck END CLOSE DriveSpace_cur DEALLOCATE DriveSpace_cur SET @HtmlOutput = ''



Server [ALL Servers]


'' + @HtmlTableHead + @HtmlOutput + @TableTail +''


''; IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END END'; IF OBJECT_ID('Inspector.ExecutionLogTruncate') IS NULL EXEC('CREATE PROCEDURE [Inspector].[ExecutionLogTruncate] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[ExecutionLogTruncate] AS BEGIN DECLARE @LastTruncate DATE; SET @LastTruncate = (SELECT TOP 1 [LastTruncate] FROM [Inspector].[ExecutionLogLastTruncate] ORDER BY [LastTruncate] DESC); IF (@LastTruncate IS NULL OR @LastTruncate < CAST(GETDATE() AS DATE)) BEGIN TRUNCATE TABLE [Inspector].[ExecutionLog]; UPDATE [Inspector].[ExecutionLogLastTruncate] SET [LastTruncate] = CAST(GETDATE() AS DATE); END END'; IF OBJECT_ID('Inspector.MemoryDumpsInsert') IS NULL BEGIN EXEC sp_executesql N'CREATE PROCEDURE [Inspector].[MemoryDumpsInsert] AS;'; END EXEC sp_executesql N'ALTER PROCEDURE [Inspector].[MemoryDumpsInsert] AS SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME; DECLARE @MemoryDumpRetentionInDays INT SET @MemoryDumpRetentionInDays = ISNULL(TRY_CAST([Inspector].[GetServerModuleThreshold](@Servername,''MemoryDumps'',''MemoryDumpsRetentionInDays'') AS INT),180); DELETE FROM [Inspector].[MemoryDumps] WHERE [Servername] = @Servername AND [creation_time] < DATEADD(DAY,-@MemoryDumpRetentionInDays,SYSDATETIMEOFFSET()) INSERT INTO [Inspector].[MemoryDumps] ([Servername],[filename],[creation_time],[size_in_mb]) SELECT @Servername, [filename], [creation_time], CAST(([size_in_bytes]/1048576.00) AS DECIMAL(18,2)) AS size_in_mb FROM sys.dm_server_memory_dumps md WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[MemoryDumps] WHERE [MemoryDumps].[creation_time] = [md].[creation_time] AND [MemoryDumps].[Servername] = @Servername );'; IF OBJECT_ID('Inspector.MemoryDumpsReport') IS NULL BEGIN EXEC sp_executesql N'CREATE PROCEDURE [Inspector].[MemoryDumpsReport] AS;'; END EXEC sp_executesql N'ALTER PROCEDURE [Inspector].[MemoryDumpsReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastReportDate DATETIME = [Inspector].[GetLastReportDateTime](@ModuleConfig); DECLARE @LastCollection DATETIME = [Inspector].[GetLastCollectionDateTime] (@Modulename); SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Recent memory dumps'', @TableHeaderColour, ''Server name,filename,creation time,size in MB'') ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= @LastReportDate) BEGIN SET @HtmlOutput =( SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], [Servername] AS ''td'','''', + CONVERT(VARCHAR(17),[creation_time],113) AS ''td'','''', + [filename] AS ''td'','''', + [size_in_mb] AS ''td'','''' FROM [Inspector].[MemoryDumps] WHERE Servername = @Servername AND [creation_time] > CAST(@LastReportDate AS DATETIMEOFFSET(7)) ORDER BY [creation_time] ASC FOR XML PATH(''tr''),ELEMENTS); IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput =( SELECT ''#FFFFFF'' AS [@bgcolor], @Servername AS ''td'','''', + ''No recent memory dumps found'' AS ''td'','''', + ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END --If @NoClutter is on we do not want to show the table if it has @InfoHighlight against the row/s IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No recent memory dumps found%'') BEGIN SET @HtmlOutput = NULL; END END IF (@HtmlOutput IS NOT NULL) BEGIN SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),Elements); --Mark Collection as out of date SET @CollectionOutOfDate = 1; SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END'; IF OBJECT_ID('Inspector.AgentJobsDesiredStateInsert') IS NULL BEGIN EXEC sp_executesql N'CREATE PROCEDURE [Inspector].[AgentJobsDesiredStateInsert] AS;'; END EXEC sp_executesql N'ALTER PROCEDURE [Inspector].[AgentJobsDesiredStateInsert] AS SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME; /* Insert jobs that do not exist in the Inspector table , assume that the current enabled flag is the desiredstate */ INSERT INTO [Inspector].[AgentJobsDesiredState] ([Servername],[JobName],[DesiredState],[Enabled],[LastChecked]) SELECT @Servername, [sysjobs].[name], CAST([sysjobs].[enabled] AS BIT), CAST([sysjobs].[enabled] AS BIT), GETDATE() FROM [msdb].[dbo].[sysjobs] WHERE NOT EXISTS (SELECT 1 FROM [Inspector].[AgentJobsDesiredState] WHERE [AgentJobsDesiredState].[JobName] = [sysjobs].[name] AND [Servername] = @Servername ); /* Update the MsdbEnabled column with the current enabled value in msdb */ UPDATE DesiredState SET [Enabled] = CAST([sysjobs].[enabled] AS BIT), [LastChecked] = GETDATE() FROM [Inspector].[AgentJobsDesiredState] DesiredState INNER JOIN [msdb].[dbo].[sysjobs] ON [DesiredState].[JobName] = [sysjobs].[name] WHERE [Servername] = @Servername; '; IF OBJECT_ID('Inspector.AgentJobsDesiredStateReport') IS NULL BEGIN EXEC sp_executesql N'CREATE PROCEDURE [Inspector].[AgentJobsDesiredStateReport] AS;'; END EXEC sp_executesql N'ALTER PROCEDURE [Inspector].[AgentJobsDesiredStateReport] ( @Servername NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7) = ''#E6E6FA'', @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @ModuleConfig VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @HtmlOutput VARCHAR(MAX) OUTPUT, @CollectionOutOfDate BIT OUTPUT, @PSCollection BIT, @Debug BIT = 0 ) AS DECLARE @HtmlTableHead VARCHAR(2000); DECLARE @LastReportDate DATETIME = [Inspector].[GetLastReportDateTime](@ModuleConfig); DECLARE @LastCollection DATETIME = [Inspector].[GetLastCollectionDateTime] (@Modulename); SET @Debug = [Inspector].[GetDebugFlag](@Debug,@ModuleConfig,@Modulename); --Set columns names for the Html table SET @HtmlTableHead = (SELECT [Inspector].[GenerateHtmlTableheader] ( @Servername, @Modulename, @ServerSpecific, ''Agent jobs not matching your desired state'', @TableHeaderColour, ''Server name,Job name,Desired state,Enabled in msdb,Last checked,Code to enable'') ); /* if there has been a data collection since the last report frequency minutes ago then run the report */ IF(@LastCollection >= @LastReportDate) BEGIN SET @HtmlOutput =( SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], [Servername] AS ''td'','''', + [JobName] AS ''td'','''', + [DesiredState] AS ''td'','''', + [Enabled] AS ''td'','''', + CONVERT(VARCHAR(17),[LastChecked],113) AS ''td'','''', + ''EXEC msdb.dbo.sp_update_job @job_name = N''''''+[JobName]+'''''', @enabled = ''+CAST([DesiredState] AS CHAR)+'';'' AS ''td'','''' FROM [Inspector].[AgentJobsDesiredState] WHERE Servername = @Servername AND [LastChecked] > @LastReportDate AND [Enabled] != [DesiredState] ORDER BY [JobName] ASC FOR XML PATH(''tr''),ELEMENTS); IF @HtmlOutput IS NULL BEGIN SET @HtmlOutput =( SELECT ''#FFFFFF'' AS [@bgcolor], @Servername AS ''td'','''', + ''No Agent jobs not matching your desired state'' AS ''td'','''', + ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''',+ ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),ELEMENTS); END --If @NoClutter is on we do not want to show the table if it has @InfoHighlight against the row/s IF (@NoClutter = 1) BEGIN IF (@HtmlOutput LIKE ''%No Agent jobs not matching your desired state%'') BEGIN SET @HtmlOutput = NULL; END END IF (@HtmlOutput IS NOT NULL) BEGIN SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END END ELSE BEGIN SET @HtmlOutput = (SELECT CASE WHEN @WarningLevel IS NULL THEN @AdvisoryHighlight WHEN @WarningLevel = 1 THEN @WarningHighlight WHEN @WarningLevel = 2 THEN @AdvisoryHighlight WHEN @WarningLevel = 3 THEN @InfoHighlight END AS [@bgcolor], @Servername AS ''td'','''', + ''Data Collection out of date'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''', + ''N/A'' AS ''td'','''' FOR XML PATH(''tr''),Elements); --Mark Collection as out of date SET @CollectionOutOfDate = 1; SET @HtmlOutput = @HtmlTableHead + @HtmlOutput + @TableTail +''


'' END IF (@Debug = 1) BEGIN SELECT OBJECT_NAME(@@PROCID) AS ''Procname'', @Servername AS ''@Servername'', @Modulename AS ''@Modulename'', @TableHeaderColour AS ''@TableHeaderColour'', @WarningHighlight AS ''@WarningHighlight'', @AdvisoryHighlight AS ''@AdvisoryHighlight'', @InfoHighlight AS ''@InfoHighlight'', @ModuleConfig AS ''@ModuleConfig'', @WarningLevel AS ''@WarningLevel'', @NoClutter AS ''@NoClutter'', @TableTail AS ''@TableTail'', @HtmlOutput AS ''@HtmlOutput'', @HtmlTableHead AS ''@HtmlTableHead'', @CollectionOutOfDate AS ''@CollectionOutOfDate'', @PSCollection AS ''@PSCollection'' END'; IF OBJECT_ID('Inspector.InspectorDataCollection') IS NULL EXEC('CREATE PROCEDURE [Inspector].[InspectorDataCollection] AS;'); EXEC sp_executesql N' ALTER PROCEDURE [Inspector].[InspectorDataCollection] ( @ModuleConfig VARCHAR(20) = NULL, @PSCollection BIT = 0, @PSExecModules BIT = 0, @PSGenerateReport BIT = 0, @PSCentralServer NVARCHAR(128) = @@SERVERNAME, @IgnoreSchedules BIT = 0 ) AS BEGIN --Revision date: 24/05/2020 SET NOCOUNT ON; DECLARE @Servername NVARCHAR(128) = @@SERVERNAME; DECLARE @CollectionProcedurename NVARCHAR(128); DECLare @FullCollectionProcedurename NVARCHAR(256); DECLARE @ModuleConfig_Desc VARCHAR(20); DECLARE @Modulename VARCHAR(128); DECLARE @CollectionStart DATETIME; DECLARE @ReportStart DATETIME = GETDATE(); DECLARE @Procname NVARCHAR(128) = OBJECT_NAME(@@PROCID); DECLARE @Frequency SMALLINT; DECLARE @Duration MONEY; DECLARE @ReportWarningsOnly TINYINT; DECLARE @ErrorMessage NVARCHAR(128); DECLARE @CachedReportID UNIQUEIDENTIFIER; IF EXISTS(SELECT 1 FROM [Inspector].[CurrentServers] WHERE [IsActive] = 1 AND Servername = @Servername) BEGIN IF EXISTS (SELECT ModuleConfig_Desc FROM [Inspector].[Modules] WHERE ModuleConfig_Desc = @ModuleConfig) OR @ModuleConfig IS NULL BEGIN --Truncate ExecutionLog if required EXEC [Inspector].[ExecutionLogTruncate]; --Check for reports due and cache them in case the collection procs executed in the cursor below exceed the current minute IF EXISTS (SELECT 1 FROM [Inspector].[ReportSchedulesDue]) BEGIN SET @CachedReportID = NEWID(); INSERT INTO [Inspector].[ReportsDueCache] (ID,ModuleConfig_Desc,CurrentScheduleStart,ReportWarningsOnly,NoClutter,Frequency,EmailGroup,EmailProfile,EmailAsAttachment) SELECT @CachedReportID,ModuleConfig_Desc,[CurrentScheduleStart],ReportWarningsOnly,NoClutter,Frequency,EmailGroup,EmailProfile,EmailAsAttachment FROM [Inspector].[ReportSchedulesDue]; END --Populate needs to occur here rather than the other @PSCollection = 1 block because the Modules table trigger is fired upon the Modules LatRunDateTime update --and we want to control the format of the data through this proc. IF @PSCollection = 1 BEGIN RAISERROR(''Populating PSConfig where required'',0,0) WITH NOWAIT; EXEC [Inspector].[PopulatePSConfig]; END --If @ModuleConfig IS NULL check if specific server has a Moduleconfig set against it and set @ModuleConfig accordingly, if none found then set ''Default'' IF (@ModuleConfig IS NULL) BEGIN SELECT @ModuleConfig = ISNULL(ModuleConfig_Desc,''Default'') FROM [Inspector].[CurrentServers] WHERE IsActive = 1 AND Servername = @Servername; END SET @ReportWarningsOnly = (SELECT ReportWarningsOnly FROM [Inspector].[ModuleConfig] WHERE ModuleConfig_Desc = @ModuleConfig); RAISERROR(''ModuleConfig selected for server: %s'',0,0,@ModuleConfig) WITH NOWAIT; IF @PSCollection = 1 BEGIN RAISERROR(''Displaying executed modules list for Powershell collection'',0,0) WITH NOWAIT; EXEC [Inspector].[PSGetConfig] @Servername = @Servername, @ModuleConfig = @ModuleConfig, @PSExecModules = @PSExecModules; END DECLARE InspectorCollection_cur CURSOR LOCAL STATIC FOR --Get enabled module list for @ModuleConfig SELECT [ModuleConfig_Desc], [Modulename], [CollectionProcedurename], [Frequency] FROM [Inspector].[ModuleSchedulesDue] WHERE [ModuleConfig_Desc] = @ModuleConfig UNION --Ignore schedule and get all Enabled modules for @ModuleConfig or all if NULL SELECT [ModuleConfig_Desc], [Modulename], [CollectionProcedurename], [Frequency] FROM [Inspector].[Modules] WHERE (@IgnoreSchedules = 1 OR @PSExecModules = 1) AND IsActive = 1 AND [CollectionProcedurename] IS NOT NULL AND [ModuleConfig_Desc] = ISNULL(@ModuleConfig,[ModuleConfig_Desc]) UNION --Get modules due for collection that are not part of @ModuleConfig but due a collection SELECT [ModuleConfig_Desc], [Modulename], [CollectionProcedurename], [Frequency] FROM [Inspector].[ModuleSchedulesDue] WHERE [ModuleConfig_Desc] != @ModuleConfig ORDER BY [CollectionProcedurename] ASC; OPEN InspectorCollection_cur FETCH NEXT FROM InspectorCollection_cur INTO @ModuleConfig_Desc,@Modulename, @CollectionProcedurename, @Frequency WHILE @@FETCH_STATUS = 0 BEGIN SET @FullCollectionProcedurename = N''Inspector.''+@CollectionProcedurename; IF OBJECT_ID(@FullCollectionProcedurename) IS NOT NULL BEGIN SET @CollectionStart = GETDATE(); RAISERROR(''Running [Inspector].[%s]'',0,0,@CollectionProcedurename) WITH NOWAIT; BEGIN TRY EXEC(''EXEC [Inspector].[''+@CollectionProcedurename+''];''); SET @ErrorMessage = NULL; END TRY BEGIN CATCH SET @ErrorMessage = CAST(ERROR_MESSAGE() AS NVARCHAR(128)); END CATCH SET @Duration = CAST(DATEDIFF(MILLISECOND,@CollectionStart,GETDATE()) AS MONEY)/1000; EXEC [Inspector].[ExecutionLogInsert] @RunDatetime = @CollectionStart, @Servername = @Servername, @ModuleConfigDesc = @ModuleConfig_Desc, @Procname = @CollectionProcedurename, @Frequency = @Frequency, @Duration = @Duration, @PSCollection = @PSCollection, @ErrorMessage = @ErrorMessage; END ELSE BEGIN RAISERROR(''No collection required for Module: %s'',0,0,@Modulename) WITH NOWAIT; END --Update LastRunDateTime UPDATE [Inspector].[Modules] SET LastRunDateTime = GETDATE() WHERE ModuleConfig_Desc = @ModuleConfig_Desc AND Modulename = @Modulename; FETCH NEXT FROM InspectorCollection_cur INTO @ModuleConfig_Desc,@Modulename, @CollectionProcedurename, @Frequency END CLOSE InspectorCollection_cur DEALLOCATE InspectorCollection_cur RAISERROR(''Running [Inspector].[InstanceStartInsert]'',0,0) WITH NOWAIT; EXEC [Inspector].[InstanceStartInsert]; RAISERROR(''Running [Inspector].[InstanceVersionInsert]'',0,0) WITH NOWAIT; EXEC [Inspector].[InstanceVersionInsert]; IF @PSCollection = 1 BEGIN RAISERROR(''Cleaning up history tables'',0,0) WITH NOWAIT; EXEC [Inspector].[PSHistCleanup]; END END ELSE BEGIN RAISERROR(''@ModuleConfig supplied: ''''%s'''' is not a valid module config description, for valid options query [Inspector].[Modules]'',11,0,@ModuleConfig); END IF @PSCentralServer = @@SERVERNAME BEGIN --Run InspectorReportMaster to pick up any scheduled reports RAISERROR(''Running [Inspector].[InspectorReportMaster]'',0,0) WITH NOWAIT; EXEC [Inspector].[InspectorReportMaster] @PSCollection = @PSCollection, @CachedReportID = @CachedReportID; END --Log InspectorDataCollection proc duration to the ExecutionLog SET @Duration = CAST(DATEDIFF(MILLISECOND,@ReportStart,GETDATE()) AS MONEY)/1000; EXEC [Inspector].[ExecutionLogInsert] @RunDatetime = @ReportStart, @Servername = @Servername, @ModuleConfigDesc = @ModuleConfig, @Procname = @Procname, @Duration = @Duration, @PSCollection = @PSCollection; END ELSE --Server not present in CurrentServer or IsActive = 1 BEGIN SET @ModuleConfig = ISNULL(@ModuleConfig,''Default''); IF @PSCollection = 1 BEGIN RAISERROR(''Cleaning up history tables'',0,0) WITH NOWAIT; EXEC [Inspector].[PSHistCleanup]; END IF @PSCentralServer = @@SERVERNAME BEGIN --Run InspectorReportMaster to pick up any scheduled reports RAISERROR(''Running [Inspector].[InspectorReportMaster]'',0,0) WITH NOWAIT; EXEC [Inspector].[InspectorReportMaster] @PSCollection = @PSCollection, @CachedReportID = @CachedReportID; END --Log InspectorDataCollection proc duration to the ExecutionLog SET @Duration = CAST(DATEDIFF(MILLISECOND,@ReportStart,GETDATE()) AS MONEY)/1000; EXEC [Inspector].[ExecutionLogInsert] @RunDatetime = @ReportStart, @Servername = @Servername, @ModuleConfigDesc = @ModuleConfig, @Procname = @Procname, @Duration = @Duration, @PSCollection = @PSCollection; RAISERROR(''Server: %s not present or IsActive = 0 in [Inspector].[CurrentServers]'',0,0,@Servername); END END'; IF OBJECT_ID('Inspector.SQLUnderCoverInspectorReport') IS NULL EXEC('CREATE PROCEDURE [Inspector].[SQLUnderCoverInspectorReport] AS;'); --Create Main Inspector Report Stored Procedure EXEC sp_executesql N' /********************************************* --Author: Adrian Buckman --Revision date: 29/05/2021 --Description: SQLUnderCoverInspectorReport - Report and email from Central logging tables. *********************************************/ ALTER PROCEDURE [Inspector].[SQLUnderCoverInspectorReport] ( @EmailDistributionGroup VARCHAR(50) = ''DBA'', @TestMode BIT = 0, @ModuleDesc VARCHAR(20) = NULL, @ReportWarningsOnly TINYINT = 0, @EmailProfile NVARCHAR(128) = NULL, @EmailAsAttachment BIT = 0, @Theme VARCHAR(5) = ''Dark'', @PSCollection BIT = 0, @NoClutter BIT = 0, @Debug BIT = 0 ) AS BEGIN SET NOCOUNT ON; IF EXISTS (SELECT 1 FROM [Inspector].[Modules] WHERE ModuleConfig_Desc = @ModuleDesc) OR @ModuleDesc IS NULL BEGIN --Check if a database filename resync is required following installation of V1.4 or manually triggered via the setting DECLARE @DatabaseFilenameSync BIT = (SELECT TRY_CONVERT(BIT,ISNULL(NULLIF([Value],''''),1)) FROM [Inspector].[Settings] WHERE [Description] = ''InspectorUpgradeFilenameSync'') IF (@DatabaseFilenameSync = 1) BEGIN EXEC [Inspector].[DatabaseGrowthFilenameSync]; END IF OBJECT_ID(''tempdb.dbo.#TrafficLightSummary'') IS NOT NULL DROP TABLE #TrafficLightSummary; CREATE TABLE #TrafficLightSummary ( SummaryHeader VARCHAR(1000), WarningPriority TINYINT ); SET @Theme = UPPER(@Theme); DECLARE @Procname NVARCHAR(128) = OBJECT_NAME(@@PROCID); DECLARE @ModuleConfig VARCHAR(20); DECLARE @ModuleConfigDetermined VARCHAR(20); DECLARE @InspectorBuild VARCHAR(6) = (SELECT ISNULL([Value],'''') FROM [Inspector].[Settings] WHERE [Description] = ''InspectorBuild''); DECLARE @ReportStart DATETIME = GETDATE(); DECLARE @ModuleReportStart DATETIME; DECLARE @SQLstatement NVARCHAR(1000); DECLARE @TotalWarningCount INT = 0; DECLARE @TotalAdvisoryCount INT = 0; DECLARE @CountWarning INT = 0; DECLARE @CountAdvisory INT = 0; DECLARE @WarningLevel TINYINT; DECLARE @WarningLevelFontColour VARCHAR(7); DECLARE @VersionNo VARCHAR(128); DECLARE @Edition VARCHAR(128); DECLARE @DatabaseGrowthCheckRunEnabled BIT; DECLARE @Duration MONEY; DECLARE @Frequency SMALLINT; DECLARE @CatalogueInstalled BIT; DECLARE @CatalogueBuild VARCHAR(10); DECLARE @MinCatalogueBuild VARCHAR(10) = ''0.2.0''; DECLARE @CatalogueModuleEnabled BIT; DECLARE @CatalogueModulename VARCHAR(20); DECLARE @CatalogueHtml VARCHAR(MAX); DECLARE @ReportModuleHtml VARCHAR(MAX); DECLARE @CountCatalogueWarnings INT; DECLARE @CatalogueLastExecution DATETIME; DECLARE @CatalogueModuleReport BIT; DECLARE @Stack VARCHAR(255) = (SELECT [Value] from [Inspector].[Settings] WHERE [Description] = ''SQLUndercoverInspectorEmailSubject''); DECLARE @EmailHeader VARCHAR(2000) = CASE WHEN @PSCollection = 0 THEN '''' ELSE '''' END; DECLARE @SubjectText VARCHAR(255); DECLARE @AlertSubjectText VARCHAR(255); DECLARE @Importance VARCHAR(6); DECLARE @HighestImportance TINYINT; DECLARE @EmailBody VARCHAR(MAX) = ''''; DECLARE @AlertHeader VARCHAR(MAX) = ''''; DECLARE @AdvisoryHeader VARCHAR(MAX) = ''''; DECLARE @InfoHeader VARCHAR(MAX) = ''''; DECLARE @AlertHeaderOutput VARCHAR(1000); DECLARE @AdvisoryHeaderOutput VARCHAR(1000); DECLARE @InfoHeaderOutput VARCHAR(1000); DECLARE @AlertOutput VARCHAR(1000) = ''''; DECLARE @RecipientsList VARCHAR(1000) = (SELECT Recipients FROM [Inspector].[EmailRecipients] WHERE [Description] = @EmailDistributionGroup) DECLARE @WarningHighlight VARCHAR(7) = (SELECT ISNULL(NULLIF([HighlightHtmlColor],''''),''#fc5858'') FROM [Inspector].[ModuleWarnings] WHERE [WarningLevel] = 1); DECLARE @AdvisoryHighlight VARCHAR(7) = (SELECT ISNULL(NULLIF([HighlightHtmlColor],''''),''#FAFCA4'') FROM [Inspector].[ModuleWarnings] WHERE [WarningLevel] = 2); DECLARE @InfoHighlight VARCHAR(7) = (SELECT ISNULL(NULLIF([HighlightHtmlColor],''''),''#FEFFFF'') FROM [Inspector].[ModuleWarnings] WHERE [WarningLevel] = 3); DECLARE @GradientLeftWarningHighlight VARCHAR(7) = (SELECT ISNULL(NULLIF([GradientLeftHtmlColor],''''),CASE WHEN @Theme = ''LIGHT'' THEN ''#FFFFFF'' ELSE ''#000000'' END) FROM [Inspector].[ModuleWarnings] WHERE [WarningLevel] = 1); DECLARE @GradientLeftAdvisoryHighlight VARCHAR(7) = (SELECT ISNULL(NULLIF([GradientLeftHtmlColor],''''),CASE WHEN @Theme = ''LIGHT'' THEN ''#FFFFFF'' ELSE ''#000000'' END) FROM [Inspector].[ModuleWarnings] WHERE [WarningLevel] = 2); DECLARE @GradientLeftInfoHighlight VARCHAR(7) = (SELECT ISNULL(NULLIF([GradientLeftHtmlColor],''''),CASE WHEN @Theme = ''LIGHT'' THEN ''#FFFFFF'' ELSE ''#000000'' END) FROM [Inspector].[ModuleWarnings] WHERE [WarningLevel] = 3); DECLARE @GradientRightWarningHighlight VARCHAR(7) = (SELECT ISNULL(NULLIF([GradientRightHtmlColor],''''),@WarningHighlight) FROM [Inspector].[ModuleWarnings] WHERE [WarningLevel] = 1); DECLARE @GradientRightAdvisoryHighlight VARCHAR(7) = (SELECT ISNULL(NULLIF([GradientRightHtmlColor],''''),@AdvisoryHighlight) FROM [Inspector].[ModuleWarnings] WHERE [WarningLevel] = 2); DECLARE @GradientRightInfoHighlight VARCHAR(7) = (SELECT ISNULL(NULLIF([GradientRightHtmlColor],''''),CASE WHEN @Theme = ''LIGHT'' THEN ''#000000'' ELSE ''#FFFFFF'' END) FROM [Inspector].[ModuleWarnings] WHERE [WarningLevel] = 3); DECLARE @TableTail VARCHAR(256) DECLARE @StandardTableTail VARCHAR(65) DECLARE @TableHeaderColour VARCHAR(7) DECLARE @ServerSummaryHeader VARCHAR(MAX) = ''SQLUndercover Inspector Build: ''+@InspectorBuild+''

Report date: ''+CONVERT(VARCHAR(17),GETDATE(),113)+''

No clutter mode: ''+CASE WHEN @NoClutter = 1 THEN ''On'' ELSE ''Off'' END+''


Server Summary:

''; DECLARE @ServerSummaryFontColour VARCHAR(30) DECLARE @DriveLetterExcludes VARCHAR(10) = (SELECT REPLACE(REPLACE([Value],'':'',''''),''\'','''') FROM [Inspector].[Settings] WHERE [Description] = ''DriveSpaceDriveLetterExcludes''); DECLARE @DisabledModules VARCHAR(2000) DECLARE @InstanceStart DATETIME DECLARE @InstanceVersionInfo NVARCHAR(128) DECLARE @InstanceUptime INT DECLARE @PhysicalServername NVARCHAR(128) DECLARE @ReportDataRetention INT = (SELECT ISNULL(NULLIF(CAST([Value] AS INT),''''),30) from [Inspector].[Settings] WHERE [Description] = ''ReportDataRetention''); DECLARE @UseMedian BIT = (SELECT ISNULL(NULLIF(CAST([Value] AS BIT),''''),0) from [Inspector].[Settings] WHERE [Description] = ''UseMedianCalculationForDriveSpaceCalc''); DECLARE @CollectionOutOfDate BIT DECLARE @ReportProcedurename NVARCHAR(128) DECLare @FullReportProcedurename NVARCHAR(256) DECLARE @Modulename VARCHAR(50) DECLARE @ServerSpecific BIT; DECLARE @ReportSummary VARCHAR(MAX) = ''''; DECLARE @DetailedSummary BIT = (SELECT CASE WHEN [Value] IS NULL OR [Value] != 1 THEN 0 ELSE 1 END FROM [Inspector].[Settings] WHERE [Description] = ''ReportDataDetailedSummary'') DECLARE @MultiWarningModule BIT; DECLARE @ShowDisabledModules BIT; DECLARE @ErrorMessage NVARCHAR(128); DECLARE @ReportId INT; DECLARE @Databasename NVARCHAR(128) = DB_NAME(); DECLARE @AttachmentQuery NVARCHAR(256); --------------Internal use only---------------------- DECLARE @DriveExtensionRequest VARCHAR(MAX) DECLARE @DriveExtensionRequestStage VARCHAR(MAX) = '''' DECLARE @DriveSpaceTableOnly VARCHAR(MAX); ----------------------------------------------------- --Replace HTML white with slight variance as white will break non warning/Advisory/Info IF @WarningHighlight = ''#FFFFFF'' BEGIN SET @WarningHighlight = ''#FEFFFF''; END IF @AdvisoryHighlight = ''#FFFFFF'' BEGIN SET @AdvisoryHighlight = ''#FEFFFF''; END IF @InfoHighlight = ''#FFFFFF'' BEGIN SET @InfoHighlight = ''#FEFFFF''; END IF @ModuleDesc IS NULL BEGIN SET @SubjectText = (SELECT [EmailSubject] FROM [Inspector].[EmailConfig] WHERE [ModuleConfig_Desc] = ''Default''); END ELSE BEGIN SET @SubjectText = (SELECT [EmailSubject] FROM [Inspector].[EmailConfig] WHERE [ModuleConfig_Desc] = @ModuleDesc); END IF @SubjectText IS NULL BEGIN SET @SubjectText = ''SQLUndercover Inspector check''; END SET @SubjectText= @SubjectText +'' for [''+ISNULL(@Stack,'''')+'']''; SET @AlertSubjectText = @SubjectText +'' - WARNINGS FOUND! ''; IF @Theme IS NOT NULL BEGIN SET @Theme = UPPER(@Theme) END; IF @Theme IS NULL BEGIN SET @Theme = ''DARK'' END; IF @Theme NOT IN (''LIGHT'',''DARK'') BEGIN SET @Theme = ''DARK'' END; IF (@ReportWarningsOnly > 3) BEGIN SET @ReportWarningsOnly = 0 END; --Check if the Undercover Catalogue is installed IF (OBJECT_ID(''Catalogue.ConfigInstances'') IS NOT NULL AND OBJECT_ID(''Catalogue.ConfigPoSH'') IS NOT NULL AND OBJECT_ID(''Catalogue.ExecutionLog'') IS NOT NULL) BEGIN SET @CatalogueInstalled = 1; END --Get Catalogue build and append to the Server summary header. IF @CatalogueInstalled = 1 BEGIN EXEC sp_executesql N''SELECT @CatalogueBuild = [ParameterValue] FROM [Catalogue].[ConfigPoSH] WHERE [ParameterName] = ''''CatalogueVersion'''';'',N''@CatalogueBuild VARCHAR(10) OUTPUT'',@CatalogueBuild = @CatalogueBuild OUTPUT EXEC sp_executesql N''SELECT TOP 1 @CatalogueLastExecution = [ExecutionDate] FROM [Catalogue].[ExecutionLog] WHERE [CompletedSuccessfully] = 1 ORDER BY [ID] DESC;'',N''@CatalogueLastExecution DATETIME OUTPUT'',@CatalogueLastExecution = @CatalogueLastExecution OUTPUT SET @ServerSummaryHeader = ''SQLUndercover Inspector Build: ''+ISNULL(@InspectorBuild,'''')+''

SQLUndercover Catalogue Build: ''+ISNULL(@CatalogueBuild,'''')+ISNULL(CASE WHEN @CatalogueBuild < @MinCatalogueBuild THEN '' (Incompatible)'' ELSE'''' END,'''')+''

Catalogue last executed: ''+ISNULL(CONVERT(VARCHAR(17),NULLIF(@CatalogueLastExecution,''1900-01-01 00:00:00.000''),113),''N/A'')+''

Report date: ''+CONVERT(VARCHAR(17),GETDATE(),113)+''

No clutter mode: ''+CASE WHEN @NoClutter = 1 THEN ''On'' ELSE ''Off'' END+''


Server Summary:

''; END --Build beginning of the HTML SET @EmailHeader = '' SQLUndercover Inspector

'' +ISNULL(@EmailHeader,'''')+''


''; --If DatabaseGrowth is enabled for any ModuleConfig to be included in this run then set the flag here IF EXISTS ( SELECT DistinctModuleConfig.[ModuleConfig_Desc] FROM ( SELECT DISTINCT COALESCE(@ModuleDesc,[ModuleConfig_Desc],''Default'') AS ModuleConfig_Desc FROM [Inspector].[CurrentServers] WHERE IsActive = 1 ) AS DistinctModuleConfig INNER JOIN [Inspector].[Modules] ON DistinctModuleConfig.ModuleConfig_Desc = Modules.ModuleConfig_Desc WHERE Modulename = ''DatabaseGrowths'' AND IsActive = 1 ) BEGIN SET @DatabaseGrowthCheckRunEnabled = 1; END DECLARE @Serverlist NVARCHAR(128) DECLARE ServerCur CURSOR LOCAL STATIC FOR SELECT Servername FROM [Inspector].[CurrentServers] WHERE IsActive = 1 AND (ModuleConfig_Desc = @ModuleDesc OR ModuleConfig_Desc IS NULL) ORDER BY Servername ASC; OPEN ServerCur FETCH NEXT FROM ServerCur INTO @Serverlist WHILE @@FETCH_STATUS = 0 BEGIN --Set Defaults if no ModuleConfig and/or tableheader colour specified in the CurrentServers table IF @ModuleConfig IS NULL BEGIN SET @ModuleConfig = ''Default'' END; IF @TableHeaderColour IS NULL BEGIN SET @TableHeaderColour = ''#E6E6FA'' END; SET @InstanceStart = NULL; SET @InstanceUptime = NULL; SET @InstanceVersionInfo = NULL; SET @PhysicalServername = NULL; SET @ModuleConfigDetermined = ISNULL(@ModuleDesc,@ModuleConfig); IF (@DetailedSummary = 1) BEGIN SET @ReportSummary += @Serverlist+''(''+@ModuleConfigDetermined+''):''+CHAR(13)+CHAR(10); END SET @ShowDisabledModules = (SELECT ISNULL([ShowDisabledModules],0) FROM [Inspector].[ModuleConfig] WHERE ModuleConfig_Desc = @ModuleConfigDetermined); IF @ModuleConfigDetermined != ''PeriodicBackupCheck'' BEGIN IF (@ShowDisabledModules = 1) BEGIN --Disabled Modules List SELECT @DisabledModules = ISNULL(STUFF(Modulename,1,2,''''),''None'') FROM ( SELECT '', ''+Modulename FROM [Inspector].[Modules] WHERE ModuleConfig_Desc = @ModuleConfigDetermined AND [IsActive] = 0 FOR XML PATH('''') ) AS DisabledModules (Modulename) END ELSE BEGIN SET @DisabledModules = ''Feature disabled''; END END IF @ModuleConfigDetermined = ''PeriodicBackupCheck'' BEGIN SET @DisabledModules = ''N/A'' END IF @DisabledModules IS NULL BEGIN SET @DisabledModules = ''None'' END; SET @InstanceStart = (SELECT [InstanceStart] FROM [Inspector].[InstanceStart] WHERE Servername = @Serverlist AND Log_Date >= CAST(GETDATE() AS DATE)); SET @InstanceUptime = (SELECT DATEDIFF(DAY,@InstanceStart,GETDATE())); SELECT @InstanceVersionInfo = [VersionInfo], @PhysicalServername = [PhysicalServername] FROM [Inspector].[InstanceVersion] WHERE Servername = @Serverlist AND Log_Date >= CAST(GETDATE() AS DATE); SELECT @EmailBody = @EmailBody + ''

Server [''+@Serverlist+'']

Instance start: ''+ISNULL(CONVERT(VARCHAR(17),@InstanceStart,113),''Not Recorded'')+'' (Uptime: ''+ISNULL(CAST(@InstanceUptime AS VARCHAR(6)),''N/A'')+CASE WHEN @InstanceUptime IS NOT NULL THEN '' Days)'' ELSE '')'' END + ''
Instance Version/Edition: ''+ISNULL(@InstanceVersionInfo,''Not Recorded'')+''
Physical Servername: ''+ISNULL(@PhysicalServername,''Not Recorded'')+''
'' +[Inspector].[GetServerInfo](@Serverlist) +''

'' +''ModuleConfig used: ''+@ModuleConfigDetermined+ ''
Disabled Modules: ''+@DisabledModules+''



'' DECLARE ReportProc_cur CURSOR LOCAL STATIC FOR SELECT [Modules].[ModuleConfig_Desc], ISNULL([ActiveServers].[TableHeaderColour],''#E6E6FA''), [Modules].[Modulename], [Modules].[ReportProcedurename], [Modules].[ServerSpecific], (SELECT Frequency FROM Inspector.ModuleConfig WHERE [ModuleConfig_Desc] = [Modules].[ModuleConfig_Desc] ) AS Frequency FROM ( SELECT [Servername], [ModuleConfig_Desc], [TableHeaderColour] FROM [Inspector].[CurrentServers] WHERE [IsActive] = 1 AND [Servername] = @Serverlist ) AS ActiveServers INNER JOIN [Inspector].[Modules] ON ([ActiveServers].[ModuleConfig_Desc] = [Modules].[ModuleConfig_Desc] OR ActiveServers.ModuleConfig_Desc IS NULL) WHERE [Modules].[IsActive] = 1 AND [ServerSpecific] = 1 AND ReportProcedurename IS NOT NULL AND [Modules].[ModuleConfig_Desc] = @ModuleConfigDetermined AND NOT EXISTS (SELECT 1 FROM [Inspector].[ModuleConfigReportExclusions] ExcludedReports WHERE [ExcludedReports].[Servername] = [ActiveServers].[Servername] AND [ExcludedReports].[Modulename] = [Modules].[Modulename] AND [ExcludedReports].[IsActive] = 1) ORDER BY [ActiveServers].[Servername] ASC, [Modules].[ReportOrder] ASC; OPEN ReportProc_cur FETCH NEXT FROM ReportProc_cur INTO @ModuleConfig,@TableHeaderColour,@Modulename,@ReportProcedurename,@ServerSpecific,@Frequency WHILE @@FETCH_STATUS = 0 BEGIN SET @FullReportProcedurename = N''Inspector.''+@ReportProcedurename; IF OBJECT_ID(@FullReportProcedurename) IS NOT NULL BEGIN SET @CountWarning = 0; SET @CountAdvisory = 0; SET @ModuleReportStart = GETDATE(); SET @CollectionOutOfDate = 0; SET @TableTail = ''
''+ISNULL([StringElement],'''')+''

Back to Top

''; SET @StandardTableTail = NULL; SET @MultiWarningModule = NULL; SET @MultiWarningModule = (SELECT 1 FROM [Inspector].[MultiWarningModules] WHERE [Modulename] = @Modulename); RAISERROR(''Reporting on Module: [%s] for server [%s] '',0,0,@Modulename,@Serverlist) WITH NOWAIT; --Only Server specific procs will be executed inside the cursor IF (@ServerSpecific = 1) BEGIN --The DriveSpace module needs to be executed slightly different to other modules. IF (@Modulename = N''DriveSpace'') BEGIN --Reset header output variables SET @AlertHeaderOutput = NULL; SET @AdvisoryHeaderOutput = NULL; SET @InfoHeaderOutput = NULL; SET @DriveSpaceTableOnly = NULL; EXEC [Inspector].[DriveSpaceReport] @Servername = @Serverlist, @Modulename = @Modulename, @TableHeaderColour = @TableHeaderColour, @WarningHighlight = @WarningHighlight, @AdvisoryHighlight = @AdvisoryHighlight, @InfoHighlight = @InfoHighlight, @DriveLetterExcludes = @DriveLetterExcludes, @ServerSpecific = @ServerSpecific, @PSCollection = @PSCollection, @ModuleConfig = @ModuleConfigDetermined, @UseMedian = @UseMedian, @NoClutter = @NoClutter, @TableTail = @TableTail, @DriveSpaceTableOnly = @DriveSpaceTableOnly OUTPUT, @CollectionOutOfDate = @CollectionOutOfDate OUTPUT, @HtmlOutput = @ReportModuleHtml OUTPUT, @Debug = @Debug; RAISERROR(''Generating header info for Module: [%s] for server [%s] '',0,0,@Modulename,@Serverlist) WITH NOWAIT; --Generate header information EXEC [Inspector].[GenerateHeaderInfo] @Servername = @Serverlist, @ModuleConfig = @ModuleConfigDetermined, @Modulename = @Modulename, @ModuleBodyText = @ReportModuleHtml, @WarningHighlight = @WarningHighlight, @AdvisoryHighlight = @AdvisoryHighlight, @InfoHighlight = @InfoHighlight, @WarningLevelFontColour = @WarningLevelFontColour, @CollectionOutOfDate = @CollectionOutOfDate, @NoClutter = @NoClutter, @Importance = @Importance OUTPUT, @WarningLevel = @WarningLevel, @ServerSpecific = @ServerSpecific, @TableTail = @StandardTableTail OUTPUT, @CountWarning = @CountWarning OUTPUT, @CountAdvisory = @CountAdvisory OUTPUT, @AlertHeader = @AlertHeaderOutput OUTPUT, @AdvisoryHeader = @AdvisoryHeaderOutput OUTPUT, @InfoHeader = @InfoHeaderOutput OUTPUT, @Debug = @Debug; --Check @Importance and set to the highest Importance seen so far SET @HighestImportance = ( SELECT CASE WHEN @HighestImportance IS NULL THEN Importance WHEN @HighestImportance > Importance THEN Importance ELSE @HighestImportance END FROM ( SELECT CASE @Importance WHEN ''High'' THEN 1 WHEN ''Normal'' THEN 2 WHEN ''Low'' THEN 3 END AS Importance ) Importance ); --If no headers were populated by the module then revert the back to top hyperlink IF @StandardTableTail IS NOT NULL BEGIN SET @ReportModuleHtml = REPLACE(@ReportModuleHtml,@TableTail,@StandardTableTail); END -- Append the Report module html to the main report body. SELECT @EmailBody = @EmailBody + ISNULL(@ReportModuleHtml,''''); --Append to the Headers --Add warning counts for summary column in ReportData table IF (@AlertHeaderOutput IS NOT NULL) BEGIN SET @AlertHeader += ISNULL(@AlertHeaderOutput,''''); SET @TotalWarningCount += @CountWarning; --Internal use only IF OBJECT_ID(''Inspector.DriveExtensionRequest'') IS NOT NULL BEGIN RAISERROR(''Executing DriveExtensionRequest with @Email = 0'',0,0) WITH NOWAIT; EXEC sp_executesql N''EXEC [Inspector].[DriveExtensionRequest] @html = @DriveSpaceTableOnly, @WarningHighlight = @WarningHighlight,@Email = 0, @EmailOutput = @DriveExtensionRequest OUTPUT;'', N''@DriveSpaceTableOnly VARCHAR(MAX), @WarningHighlight VARCHAR(7), @DriveExtensionRequest VARCHAR(MAX) OUTPUT'', @DriveSpaceTableOnly = @DriveSpaceTableOnly, @WarningHighlight = @WarningHighlight, @DriveExtensionRequest = @DriveExtensionRequest OUTPUT; SET @DriveExtensionRequestStage = @DriveExtensionRequestStage + ISNULL(@DriveExtensionRequest,''''); END END IF (@AdvisoryHeaderOutput IS NOT NULL) BEGIN SET @AdvisoryHeader += ISNULL(@AdvisoryHeaderOutput,''''); SET @TotalAdvisoryCount += @CountAdvisory; END IF (@InfoHeaderOutput IS NOT NULL) BEGIN --No count required here just append to header SET @InfoHeader += ISNULL(@InfoHeaderOutput,''''); END END ELSE --for all other modules use this block BEGIN --Reset header output variables SET @AlertHeaderOutput = NULL; SET @AdvisoryHeaderOutput = NULL; SET @InfoHeaderOutput = NULL; --Get Module warning level SELECT @WarningLevel = [Inspector].[GetWarningLevel](@ModuleConfigDetermined, @Modulename); BEGIN TRY SET @SQLstatement = N'' EXEC [Inspector].[''+@ReportProcedurename+''] @Servername = @Serverlist, @Modulename = @Modulename, @TableHeaderColour = @TableHeaderColour, @WarningHighlight = @WarningHighlight, @AdvisoryHighlight = @AdvisoryHighlight, @InfoHighlight = @InfoHighlight, @PSCollection = @PSCollection, @ModuleConfig = @ModuleConfigDetermined, @WarningLevel = @WarningLevel, @ServerSpecific = @ServerSpecific, @NoClutter = @NoClutter, @TableTail = @TableTail, @CollectionOutOfDate = @CollectionOutOfDate OUTPUT, @HtmlOutput = @ReportModuleHtml OUTPUT, @Debug = @Debug;'' EXEC sp_executesql @SQLstatement, N''@Serverlist NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7), @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @PSCollection BIT, @ModuleConfigDetermined VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @CollectionOutOfDate BIT OUTPUT, @ReportModuleHtml VARCHAR(MAX) OUTPUT, @Debug BIT'', @Serverlist = @Serverlist, @Modulename = @Modulename, @TableHeaderColour = @TableHeaderColour, @WarningHighlight = @WarningHighlight, @AdvisoryHighlight = @AdvisoryHighlight, @InfoHighlight = @InfoHighlight, @PSCollection = @PSCollection, @ModuleConfigDetermined = @ModuleConfigDetermined, @WarningLevel = @WarningLevel, @ServerSpecific = @ServerSpecific, @NoClutter = @NoClutter, @TableTail = @TableTail, @CollectionOutOfDate = @CollectionOutOfDate OUTPUT, @ReportModuleHtml = @ReportModuleHtml OUTPUT, @Debug = @Debug; SET @ErrorMessage = NULL; END TRY BEGIN CATCH SET @ErrorMessage = CAST(ERROR_MESSAGE() AS NVARCHAR(128)); END CATCH RAISERROR(''Generating header info for Module: [%s] for server [%s] '',0,0,@Modulename,@Serverlist) WITH NOWAIT; --Generate header information EXEC [Inspector].[GenerateHeaderInfo] @Servername = @Serverlist, @ModuleConfig = @ModuleConfigDetermined, @Modulename = @Modulename, @ModuleBodyText = @ReportModuleHtml, @WarningHighlight = @WarningHighlight, @AdvisoryHighlight = @AdvisoryHighlight, @InfoHighlight = @InfoHighlight, @WarningLevelFontColour = @WarningLevelFontColour, @CollectionOutOfDate = @CollectionOutOfDate, @NoClutter = @NoClutter, @Importance = @Importance OUTPUT, @WarningLevel = @WarningLevel, @ServerSpecific = @ServerSpecific, @TableTail = @StandardTableTail OUTPUT, @CountWarning = @CountWarning OUTPUT, @CountAdvisory = @CountAdvisory OUTPUT, @AlertHeader = @AlertHeaderOutput OUTPUT, @AdvisoryHeader = @AdvisoryHeaderOutput OUTPUT, @InfoHeader = @InfoHeaderOutput OUTPUT, @Debug = @Debug; --Check @Importance and set to the highest Importance seen so far SET @HighestImportance = ( SELECT CASE WHEN @HighestImportance IS NULL THEN Importance WHEN @HighestImportance > Importance THEN Importance ELSE @HighestImportance END FROM ( SELECT CASE @Importance WHEN ''High'' THEN 1 WHEN ''Normal'' THEN 2 WHEN ''Low'' THEN 3 END AS Importance ) Importance ); --If no headers were populated by the module then revert the back to top hyperlink IF @StandardTableTail IS NOT NULL BEGIN SET @ReportModuleHtml = REPLACE(@ReportModuleHtml,@TableTail,@StandardTableTail); END -- Append the Report module html to the main report body. SELECT @EmailBody = @EmailBody + ISNULL(@ReportModuleHtml,''''); --Append to the Headers --Add warning counts for summary column in ReportData table IF (@WarningLevel = 1 OR @MultiWarningModule = 1) BEGIN SET @AlertHeader += ISNULL(@AlertHeaderOutput,''''); SET @TotalWarningCount += @CountWarning; END IF (@WarningLevel = 2 OR @MultiWarningModule = 1) BEGIN SET @AdvisoryHeader += ISNULL(@AdvisoryHeaderOutput,''''); SET @TotalAdvisoryCount += @CountAdvisory; END IF (@WarningLevel = 3 OR @MultiWarningModule = 1) BEGIN --No count required here just append to header SET @InfoHeader += ISNULL(@InfoHeaderOutput,''''); END END IF (@DetailedSummary = 1) BEGIN IF (@Modulename IS NOT NULL) BEGIN SET @ReportSummary += '' ''+@Modulename+'': Warnings(''+CAST(ISNULL(@CountWarning,0) AS VARCHAR(10))+'') Advisories(''+CAST(ISNULL(@CountAdvisory,0) AS VARCHAR(10))+'')''+CHAR(13)+CHAR(10); END END SET @Duration = CAST(DATEDIFF(MILLISECOND,@ModuleReportStart,GETDATE()) AS MONEY)/1000; EXEC [Inspector].[ExecutionLogInsert] @RunDatetime = @ModuleReportStart, @Servername = @Serverlist, @ModuleConfigDesc = @ModuleConfig, @Procname = @ReportProcedurename, @Frequency = @Frequency, @Duration = @Duration, @PSCollection = @PSCollection, @ErrorMessage = @ErrorMessage; END END ELSE BEGIN RAISERROR(''No Report procedure found for Module: %s'',0,0,@Modulename,@Serverlist) WITH NOWAIT; EXEC [Inspector].[ExecutionLogInsert] @RunDatetime = @ModuleReportStart, @Servername = @Serverlist, @ModuleConfigDesc = @ModuleConfig, @Procname = @ReportProcedurename, @Frequency = @Frequency, @Duration = 0, @PSCollection = @PSCollection, @ErrorMessage = ''No Report procedure found for Module''; END FETCH NEXT FROM ReportProc_cur INTO @ModuleConfig,@TableHeaderColour,@Modulename,@ReportProcedurename,@ServerSpecific,@Frequency END CLOSE ReportProc_cur DEALLOCATE ReportProc_cur --Only populate the Advisory header with Version/Edition changes if this run is not the PeriodicBackupCheck IF ISNULL(@ModuleDesc,@ModuleConfig) != ''PeriodicBackupCheck'' BEGIN --Check for Instance version or edition changes in [Inspector].[InstanceVersionHistory] --Excluded from Warning level control SET @VersionNo = NULL; SET @Edition = NULL; SELECT @VersionNo = CAST([VersionNo] AS VARCHAR(128)), @Edition = CAST([Edition] AS VARCHAR(128)) FROM [Inspector].[InstanceVersionHistory] WHERE [Servername] = @Serverlist AND [CollectionDatetime] >= DATEADD(DAY,-1,GETDATE()); --If version has changed then create an entry in the advisory header IF @VersionNo IS NOT NULL BEGIN SET @AdvisoryHeader = @AdvisoryHeader + ''''+@Serverlist+'' - SQL ''+@VersionNo+''

''; END --If Edition has changed then create an entry in the advisory header IF @Edition IS NOT NULL BEGIN SET @AdvisoryHeader = @AdvisoryHeader + ''''+@Serverlist+'' - SQL ''+@Edition+''

''; END END IF @AlertHeader LIKE ''%''+@Serverlist+''%'' BEGIN SET @ServerSummaryFontColour = '''' END ELSE IF @AdvisoryHeader LIKE ''%''+@Serverlist+''%'' BEGIN SET @ServerSummaryFontColour = '''' END ELSE IF @InfoHeader LIKE ''%''+@Serverlist+''%'' BEGIN SET @ServerSummaryFontColour = '''' END ELSE BEGIN SET @ServerSummaryFontColour = '''' END --Evaluate server and colour code accordingly INSERT INTO #TrafficLightSummary ([SummaryHeader],[WarningPriority]) SELECT CASE WHEN @ServerSummaryFontColour = '''' THEN ''''+ @ServerSummaryFontColour+''''+''[''+''''+@Serverlist+''] '' WHEN @ServerSummaryFontColour = '''' THEN ''''+ @ServerSummaryFontColour+''''+''[''+''''+@Serverlist+''] '' WHEN @ServerSummaryFontColour = '''' THEN ''''+ @ServerSummaryFontColour+''''+''[''+''''+@Serverlist+''] '' WHEN @ServerSummaryFontColour = '''' THEN ''''+ @ServerSummaryFontColour+''''+''[''+''''+@Serverlist+''] '' END, CASE WHEN @ServerSummaryFontColour = '''' THEN 1 WHEN @ServerSummaryFontColour = '''' THEN 2 WHEN @ServerSummaryFontColour = '''' THEN 3 WHEN @ServerSummaryFontColour = '''' THEN 4 END --Add Break to the end of the Server warning ready for the next IF @AlertHeader LIKE ''%''+@Serverlist+''%'' BEGIN SET @AlertHeader = @AlertHeader + ''

'' END --Add Break to the end of the Server Advisory Condition ready for the next IF @AdvisoryHeader LIKE ''%''+@Serverlist+''%'' BEGIN SET @AdvisoryHeader = @AdvisoryHeader + ''

'' END --Add Break to the end of the Server Info header ready for the next IF @InfoHeader LIKE ''%''+@Serverlist+''%'' BEGIN SET @InfoHeader = @InfoHeader + ''

'' END FETCH NEXT FROM ServerCur INTO @Serverlist END CLOSE ServerCur DEALLOCATE ServerCur --Internal use Only IF OBJECT_ID(''Inspector.DriveExtensionRequest'') IS NOT NULL BEGIN IF @DriveExtensionRequest IS NOT NULL BEGIN RAISERROR(''Executing DriveExtensionRequest with @Email = 1'',0,0) WITH NOWAIT; EXEC sp_executesql N''EXEC [Inspector].[DriveExtensionRequest] @html = @DriveExtensionRequestStage, @WarningHighlight = @WarningHighlight,@Email = 1, @EmailOutput = @DriveExtensionRequest OUTPUT'', N''@DriveExtensionRequestStage VARCHAR(MAX), @WarningHighlight VARCHAR(7), @DriveExtensionRequest VARCHAR(MAX) OUTPUT'', @DriveExtensionRequestStage = @DriveExtensionRequestStage, @WarningHighlight = @WarningHighlight, @DriveExtensionRequest = @DriveExtensionRequest OUTPUT; END END IF (@DetailedSummary = 1) BEGIN SET @ReportSummary += ''ALL_SERVERS (''+@ModuleConfigDetermined+''):''+CHAR(13)+CHAR(10); END SET @ErrorMessage = NULL; DECLARE OneoffsReportProc_cur CURSOR LOCAL STATIC FOR SELECT [ModuleConfig_Desc], [Modulename], [ReportProcedurename], [ServerSpecific], [Frequency] FROM [Inspector].[GetNonServerSpecificModules](@ModuleConfigDetermined,@DatabaseGrowthCheckRunEnabled) ORDER BY [ReportOrder] ASC OPEN OneoffsReportProc_cur FETCH NEXT FROM OneoffsReportProc_cur INTO @ModuleConfig,@Modulename,@ReportProcedurename,@ServerSpecific,@Frequency WHILE @@FETCH_STATUS = 0 BEGIN SET @FullReportProcedurename = N''Inspector.''+@ReportProcedurename; IF OBJECT_ID(@FullReportProcedurename) IS NOT NULL BEGIN RAISERROR(''Reporting on Module: [%s] for server [%s] '',0,0,@Modulename,@Serverlist) WITH NOWAIT; --Reset header output variables SET @AlertHeaderOutput = NULL; SET @AdvisoryHeaderOutput = NULL; SET @InfoHeaderOutput = NULL; SET @ModuleReportStart = GETDATE(); SET @TableTail = ''

Back to Top

''; SET @MultiWarningModule = NULL; SET @MultiWarningModule = (SELECT 1 FROM [Inspector].[MultiWarningModules] WHERE [Modulename] = @Modulename); --Get Module warning level SELECT @WarningLevel = [Inspector].[GetWarningLevel](@ModuleConfigDetermined, @Modulename); BEGIN TRY SET @SQLstatement = N'' EXEC [Inspector].[''+@ReportProcedurename+''] @Servername = @Serverlist, @Modulename = @Modulename, @TableHeaderColour = @TableHeaderColour, @WarningHighlight = @WarningHighlight, @AdvisoryHighlight = @AdvisoryHighlight, @InfoHighlight = @InfoHighlight, @PSCollection = @PSCollection, @ModuleConfig = @ModuleConfigDetermined, @WarningLevel = @WarningLevel, @ServerSpecific = @ServerSpecific, @NoClutter = @NoClutter, @TableTail = @TableTail, @CollectionOutOfDate = @CollectionOutOfDate OUTPUT, @HtmlOutput = @ReportModuleHtml OUTPUT, @Debug = @Debug;'' EXEC sp_executesql @SQLstatement, N''@Serverlist NVARCHAR(128), @Modulename VARCHAR(50), @TableHeaderColour VARCHAR(7), @WarningHighlight VARCHAR(7), @AdvisoryHighlight VARCHAR(7), @InfoHighlight VARCHAR(7), @PSCollection BIT, @ModuleConfigDetermined VARCHAR(20), @WarningLevel TINYINT, @ServerSpecific BIT, @NoClutter BIT, @TableTail VARCHAR(256), @CollectionOutOfDate BIT OUTPUT, @ReportModuleHtml VARCHAR(MAX) OUTPUT, @Debug BIT'', @Serverlist = @Serverlist, @Modulename = @Modulename, @TableHeaderColour = @TableHeaderColour, @WarningHighlight = @WarningHighlight, @AdvisoryHighlight = @AdvisoryHighlight, @InfoHighlight = @InfoHighlight, @PSCollection = @PSCollection, @ModuleConfigDetermined = @ModuleConfigDetermined, @WarningLevel = @WarningLevel, @ServerSpecific = @ServerSpecific, @NoClutter = @NoClutter, @TableTail = @TableTail, @CollectionOutOfDate = @CollectionOutOfDate OUTPUT, @ReportModuleHtml = @ReportModuleHtml OUTPUT, @Debug = @Debug; SET @ErrorMessage = NULL; END TRY BEGIN CATCH SET @ErrorMessage = CAST(ERROR_MESSAGE() AS NVARCHAR(128)); END CATCH SET @Duration = CAST(DATEDIFF(MILLISECOND,@ModuleReportStart,GETDATE()) AS MONEY)/1000; EXEC [Inspector].[ExecutionLogInsert] @RunDatetime = @ModuleReportStart, @Servername = @Serverlist, @ModuleConfigDesc = @ModuleConfig, @Procname = @ReportProcedurename, @Frequency = @Frequency, @Duration = @Duration, @PSCollection = @PSCollection, @ErrorMessage = @ErrorMessage; --Generate header information EXEC [Inspector].[GenerateHeaderInfo] @Servername = ''ALL_SERVERS'', @ModuleConfig = @ModuleConfigDetermined, @Modulename = @Modulename, @ModuleBodyText = @ReportModuleHtml, @WarningHighlight = @WarningHighlight, @AdvisoryHighlight = @AdvisoryHighlight, @InfoHighlight = @InfoHighlight, @WarningLevelFontColour = @WarningLevelFontColour, @CollectionOutOfDate = @CollectionOutOfDate, @NoClutter = @NoClutter, @Importance = @Importance OUTPUT, @WarningLevel = @WarningLevel, @ServerSpecific = @ServerSpecific, @TableTail = @StandardTableTail OUTPUT, @CountWarning = @CountWarning OUTPUT, @CountAdvisory = @CountAdvisory OUTPUT, @AlertHeader = @AlertHeaderOutput OUTPUT, @AdvisoryHeader = @AdvisoryHeaderOutput OUTPUT, @InfoHeader = @InfoHeaderOutput OUTPUT, @Debug = @Debug; --Check @Importance and set to the highest Importance seen so far SET @HighestImportance = ( SELECT CASE WHEN @HighestImportance IS NULL THEN Importance WHEN @HighestImportance > Importance THEN Importance ELSE @HighestImportance END FROM ( SELECT CASE @Importance WHEN ''High'' THEN 1 WHEN ''Normal'' THEN 2 WHEN ''Low'' THEN 3 END AS Importance ) Importance ); IF (@DetailedSummary = 1) BEGIN IF @Modulename IS NOT NULL BEGIN SET @ReportSummary += '' ''+@Modulename+'': Warnings(''+CAST(ISNULL(@CountWarning,0) AS VARCHAR(10))+'') Advisories(''+CAST(ISNULL(@CountAdvisory,0) AS VARCHAR(10))+'')''+CHAR(13)+CHAR(10); END END --If no headers were populated by the module then revert the back to top hyperlink IF @StandardTableTail IS NOT NULL BEGIN SET @ReportModuleHtml = REPLACE(@ReportModuleHtml,@TableTail,@StandardTableTail); END -- Append the Report module html to the main report body. SELECT @EmailBody = @EmailBody + ISNULL(@ReportModuleHtml,''''); --Append to the Headers --Add warning counts for summary column in ReportData table IF (@WarningLevel = 1 OR @MultiWarningModule = 1) BEGIN SET @AlertHeader += ISNULL(@AlertHeaderOutput,''''); SET @TotalWarningCount += @CountWarning; END IF (@WarningLevel = 2 OR @MultiWarningModule = 1) BEGIN SET @AdvisoryHeader += ISNULL(@AdvisoryHeaderOutput,''''); SET @TotalAdvisoryCount += @CountAdvisory; END IF (@WarningLevel = 3 OR @MultiWarningModule = 1) BEGIN --No count required here just append to header SET @InfoHeader += ISNULL(@InfoHeaderOutput,''''); END END ELSE BEGIN RAISERROR(''No Report procedure found for Module: %s'',0,0,@Modulename,@Serverlist) WITH NOWAIT; EXEC [Inspector].[ExecutionLogInsert] @RunDatetime = @ModuleReportStart, @Servername = @Serverlist, @ModuleConfigDesc = @ModuleConfig, @Procname = @ReportProcedurename, @Frequency = @Frequency, @Duration = 0, @PSCollection = @PSCollection, @ErrorMessage = ''No Report procedure found for Module''; END FETCH NEXT FROM OneoffsReportProc_cur INTO @ModuleConfig,@Modulename,@ReportProcedurename,@ServerSpecific,@Frequency END CLOSE OneoffsReportProc_cur DEALLOCATE OneoffsReportProc_cur IF @Importance = ''High'' BEGIN SET @SubjectText = @AlertSubjectText; END IF @AlertHeader != '''' BEGIN SET @AlertHeader = ''

Warning Conditions:

'' +@AlertHeader END ELSE BEGIN SET @AlertHeader = ''

No Warnings are present

'' END IF @AdvisoryHeader != '''' BEGIN SET @AdvisoryHeader = ''


Advisory Conditions:

''+@AdvisoryHeader END ELSE BEGIN SET @AdvisoryHeader = ''


No Advisories are present

'' END IF @InfoHeader != '''' BEGIN SET @InfoHeader = ''


Informational Conditions:

''+@InfoHeader END ELSE BEGIN SET @InfoHeader = ''


No Informational conditions are present

'' END --Red IF EXISTS (SELECT SummaryHeader FROM #TrafficLightSummary WHERE WarningPriority = 1 ) BEGIN SELECT @ServerSummaryHeader = @ServerSummaryHeader + (SELECT STUFF(SummaryHeader,1,0,''Warnings Present -

'') FROM ( SELECT SummaryHeader + '' '' FROM #TrafficLightSummary WHERE WarningPriority = 1 FOR XML PATH('''') ) AS SummaryHeader(SummaryHeader) ) END --Amber IF EXISTS (SELECT SummaryHeader FROM #TrafficLightSummary WHERE WarningPriority = 2 ) BEGIN SELECT @ServerSummaryHeader = @ServerSummaryHeader + ''

'' + (SELECT STUFF(SummaryHeader,1,0,''Advisories/No Warnings -

'') FROM ( SELECT SummaryHeader + '' '' FROM #TrafficLightSummary WHERE WarningPriority = 2 FOR XML PATH('''') ) AS SummaryHeader(SummaryHeader) ) END --White IF EXISTS (SELECT SummaryHeader FROM #TrafficLightSummary WHERE WarningPriority = 3 ) BEGIN SELECT @ServerSummaryHeader = @ServerSummaryHeader + ''

'' + (SELECT STUFF(SummaryHeader,1,0,''Informational/No Advisories or Warnings -

'') FROM ( SELECT SummaryHeader + '' '' FROM #TrafficLightSummary WHERE WarningPriority = 3 FOR XML PATH('''') ) AS SummaryHeader(SummaryHeader) ) END --Green IF EXISTS (SELECT SummaryHeader FROM #TrafficLightSummary WHERE WarningPriority = 4 ) BEGIN SELECT @ServerSummaryHeader = @ServerSummaryHeader + ''

'' + (SELECT STUFF(SummaryHeader,1,0,''OK -

'') FROM ( SELECT SummaryHeader + '' '' FROM #TrafficLightSummary WHERE WarningPriority = 4 FOR XML PATH('''') ) AS SummaryHeader(SummaryHeader) ) END /* Reuse the ServerSummaryHeader parameter setting to a new value */ SET @ServerSummaryHeader = ''
''+@ServerSummaryHeader+''
'' + ''


''+ISNULL(@AlertHeader,'''') +''
'' + ISNULL(@AdvisoryHeader,'''') + ''
'' + ISNULL(@InfoHeader,'''') + ''
''; SET @EmailBody = @ServerSummaryHeader + @EmailBody; SET @EmailBody = Replace(Replace(@EmailBody,''<'',''<''),''>'',''>''); SET @EmailBody = @EmailHeader + @EmailBody + '' ''; IF (@EmailAsAttachment = 1) BEGIN SET @ServerSummaryHeader = @EmailHeader + @ServerSummaryHeader+ ''
See attachment for the complete report
''; SET @ServerSummaryHeader = Replace(Replace(@ServerSummaryHeader,''<'',''<''),''>'',''>''); END; IF (@DetailedSummary = 1) BEGIN SET @ReportSummary = STUFF(@ReportSummary,1,0,''Total: Warnings (''+CAST(@TotalWarningCount AS VARCHAR(6))+''), Advisories (''+CAST(@TotalAdvisoryCount AS VARCHAR(6))+'')''+CHAR(13)+CHAR(10)); END ELSE BEGIN SET @ReportSummary = ''Total: Warnings (''+CAST(@TotalWarningCount AS VARCHAR(6))+''), Advisories (''+CAST(@TotalAdvisoryCount AS VARCHAR(6))+'')''; END IF @ModuleDesc IS NULL BEGIN SET @ModuleDesc = ''NULL'' END; SET @Importance = (SELECT CASE @HighestImportance WHEN 1 THEN ''High'' WHEN 2 THEN ''Normal'' WHEN 3 THEN ''Low'' ELSE ''Low'' END); IF @TestMode = 1 OR (@RecipientsList IS NULL OR @RecipientsList = '''') BEGIN IF (@ReportWarningsOnly > 0) BEGIN IF (@HighestImportance <= @ReportWarningsOnly) BEGIN INSERT INTO [Inspector].[ReportData] (ReportDate,ModuleConfig,ReportData,Summary,Importance,EmailGroup,ReportWarningsOnly) SELECT GETDATE(),ISNULL(@ModuleDesc,@ModuleConfig),@EmailBody,CAST(@ReportSummary AS XML),@Importance,@EmailDistributionGroup,@ReportWarningsOnly; END END ELSE BEGIN INSERT INTO [Inspector].[ReportData] (ReportDate,ModuleConfig,ReportData,Summary,Importance,EmailGroup,ReportWarningsOnly) SELECT GETDATE(),ISNULL(@ModuleDesc,@ModuleConfig),@EmailBody,CAST(@ReportSummary AS XML),@Importance,@EmailDistributionGroup,@ReportWarningsOnly; END END ELSE BEGIN BEGIN TRY IF (@ReportWarningsOnly > 0) BEGIN IF (@HighestImportance <= @ReportWarningsOnly) BEGIN INSERT INTO [Inspector].[ReportData] (ReportDate,ModuleConfig,ReportData,Summary,Importance,EmailGroup,ReportWarningsOnly) SELECT GETDATE(),ISNULL(@ModuleDesc,@ModuleConfig),@EmailBody,CAST(@ReportSummary AS XML),@Importance,@EmailDistributionGroup,@ReportWarningsOnly; SELECT @ReportId = SCOPE_IDENTITY(); IF (@EmailAsAttachment = 1) BEGIN SET @AttachmentQuery = N''EXEC sp_executesql N''''SET NOCOUNT ON; SELECT TOP (1) [ReportData] FROM [Inspector].[ReportData] WHERE [ID] = @ReportID'''',N''''@ReportID INT'''',@ReportID = ''+CAST(@ReportId AS NVARCHAR(20))+'';''; EXEC msdb.dbo.sp_send_dbmail @recipients = @RecipientsList, @subject = @SubjectText, @importance = @Importance, @body = @ServerSummaryHeader, /* We only want to add the header to the email body as we attach the report as a file */ @body_format = ''HTML'', @profile_name = @EmailProfile, @attach_query_result_as_file = 1, @query_attachment_filename = N''Inspector.html'', @query = @AttachmentQuery, @execute_query_database = @Databasename, @query_result_width = 32767, @query_no_truncate = 1, @query_result_header = 1; END ELSE BEGIN EXEC msdb.dbo.sp_send_dbmail @recipients = @RecipientsList, @subject = @SubjectText, @importance = @Importance, @body=@EmailBody , @body_format = ''HTML'', @profile_name = @EmailProfile; END END END ELSE BEGIN INSERT INTO [Inspector].[ReportData] (ReportDate,ModuleConfig,ReportData,Summary,Importance,EmailGroup,ReportWarningsOnly) SELECT GETDATE(),ISNULL(@ModuleDesc,@ModuleConfig),@EmailBody,CAST(@ReportSummary AS XML),@Importance,@EmailDistributionGroup,@ReportWarningsOnly; SELECT @ReportId = SCOPE_IDENTITY(); IF (@EmailAsAttachment = 1) BEGIN SET @AttachmentQuery = N''EXEC sp_executesql N''''SET NOCOUNT ON; SELECT TOP (1) [ReportData] FROM [Inspector].[ReportData] WHERE [ID] = @ReportID'''',N''''@ReportID INT'''',@ReportID = ''+CAST(@ReportId AS NVARCHAR(20))+'';''; EXEC msdb.dbo.sp_send_dbmail @recipients = @RecipientsList, @subject = @SubjectText, @importance = @Importance, @body = @ServerSummaryHeader, /* We only want to add the header to the email body as we attach the report as a file */ @body_format = ''HTML'', @profile_name = @EmailProfile, @attach_query_result_as_file = 1, @query_attachment_filename = N''Inspector.html'', @query = @AttachmentQuery, @execute_query_database = @Databasename, @query_result_width = 32767, @query_no_truncate = 1, @query_result_header = 1; END ELSE BEGIN EXEC msdb.dbo.sp_send_dbmail @recipients = @RecipientsList, @subject = @SubjectText, @importance = @Importance, @body=@EmailBody , @body_format = ''HTML'', @profile_name = @EmailProfile; END END END TRY BEGIN CATCH SET @ErrorMessage = N''Error occured whilst executing sp_send_dbmail: ''+ CAST(ERROR_MESSAGE() AS NVARCHAR(80)); RAISERROR(''%s'',0,0,@ErrorMessage); --Severity is 0 as we are logging the error in the next code block END CATCH END SET @Duration = CAST(DATEDIFF(MILLISECOND,@ReportStart,GETDATE()) AS MONEY)/1000; IF @ModuleDesc IS NULL BEGIN SET @ModuleDesc = @ModuleConfig END EXEC [Inspector].[ExecutionLogInsert] @RunDatetime = @ReportStart, @Servername = @@SERVERNAME, @ModuleConfigDesc = @ModuleDesc, @Procname = @Procname, @Duration = @Duration, @PSCollection = @PSCollection, @ErrorMessage = @ErrorMessage; --Report Data cleanup DELETE FROM [Inspector].[ReportData] WHERE ReportDate < DATEADD(DAY,-@ReportDataRetention,GETDATE()); END ELSE BEGIN RAISERROR(''@ModuleDesc supplied does not exist in [Inspector].[Modules]'',15,1) END END'; SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' -- ============================================= -- Get a list of all non server specific modules with DatabaseGrowth and BackupSpace modules forced to look at the Default -- moduleconfig rows for report order , all custom non server specific modules -- ============================================= CREATE FUNCTION [Inspector].[GetNonServerSpecificModules] ( @ModuleDesc VARCHAR(20), @DatabaseGrowthCheckRunEnabled BIT ) --Revision date: 13/04/2020 RETURNS TABLE AS RETURN ( SELECT DISTINCT [ModuleConfig_Desc], [Modulename], [ReportProcedurename], [ServerSpecific], [ReportOrder], (SELECT [Frequency] FROM [Inspector].[ModuleConfig] WHERE NonServerSpecificModules.[ModuleConfig_Desc] = [ModuleConfig_Desc] ) AS [Frequency] FROM ( SELECT [Modules].[ModuleConfig_Desc], CASE WHEN [Modulename] = ''DatabaseGrowths'' AND (@DatabaseGrowthCheckRunEnabled IS NULL OR @DatabaseGrowthCheckRunEnabled = 0) THEN NULL ELSE [Modulename] END AS [Modulename], [ReportProcedurename], [ServerSpecific], [ReportOrder] FROM ( --All Active servers and their ModuleConfig as ServerSpecific = 0 reports on ALL active servers SELECT [Servername], [ModuleConfig_Desc], [TableHeaderColour] FROM [Inspector].[CurrentServers] WHERE [IsActive] = 1 ) AS ActiveServers INNER JOIN [Inspector].[Modules] ON ([ActiveServers].[ModuleConfig_Desc] = [Modules].[ModuleConfig_Desc] OR ActiveServers.ModuleConfig_Desc IS NULL) WHERE [Modules].[IsActive] = 1 AND ReportProcedurename IS NOT NULL AND [ServerSpecific] = 0 AND [Modules].[ModuleConfig_Desc] = @ModuleDesc ) AS NonServerSpecificModules WHERE [Modulename] IS NOT NULL )' EXEC(@SQLStatement); --Agent job creation SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' USE [msdb]; IF NOT EXISTS (SELECT name FROM sysjobs WHERE name = ''Inspector Auto ['+@Databasename+']'') BEGIN BEGIN TRANSACTION DECLARE @ReturnCode INT SELECT @ReturnCode = 0 IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N''[Uncategorized (Local)]'' AND category_class=1) BEGIN EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N''JOB'', @type=N''LOCAL'', @name=N''[Uncategorized (Local)]'' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback END DECLARE @jobId BINARY(16) EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N''Inspector Auto ['+@Databasename+']'', @enabled='+CASE WHEN @EnableAgentJob = 1 THEN '1' ELSE '0' END+', @notify_level_eventlog=0, @notify_level_email=0, @notify_level_netsend=0, @notify_level_page=0, @delete_level=0, @description=N''SQLUndercover Inspector Auto will check for Modules due to be executed from the Modules table and Reports due to be created from the ModuleConfig table'', @category_name=N''[Uncategorized (Local)]'', @owner_login_name=N''sa'', @job_id = @jobId OUTPUT IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N''Collect data and report on schedules automatically'', @step_id=1, @cmdexec_success_code=0, @on_success_action=1, @on_success_step_id=0, @on_fail_action=2, @on_fail_step_id=0, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N''TSQL'', @command=N''EXEC ['+@Databasename+'].[Inspector].[InspectorDataCollection] @ModuleConfig = NULL,@IgnoreSchedules = 0,@PSCollection = 0;'', @database_name=N''master'', @flags=0 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId, @name=N''Every minute'', @enabled=1, @freq_type=4, @freq_interval=1, @freq_subday_type=4, @freq_subday_interval=1, @freq_relative_interval=0, @freq_recurrence_factor=0, @active_start_date=20191006, @active_end_date=99991231, @active_start_time=0, @active_end_time=235959 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N''(local)'' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback COMMIT TRANSACTION GOTO EndSave QuitWithRollback: IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION EndSave: END' EXEC (@SQLStatement); --Update job command to use Collection stored procedure introduced with issue #13 --Tidied Agent job descriptions and changed equality job searches to LIKE searches as some users may rename jobs slightly , such as prefixing or suffixing. SET @SQLStatement = CONVERT(NVARCHAR(MAX), '')+N' DECLARE @JobID UNIQUEIDENTIFIER = NULL SET @JobID = (SELECT job_id FROM msdb.dbo.sysjobs WHERE name LIKE ''%SQLUndercover Inspector Data Collection%'') IF @JobID IS NOT NULL BEGIN EXEC msdb.dbo.sp_update_jobstep @job_id = @JobID, @step_id=1 ,@command=N''EXEC ['+@Databasename+'].[Inspector].[InspectorDataCollection] @ModuleConfig = NULL;''; END SET @JobID = NULL SET @JobID = (SELECT job_id FROM msdb.dbo.sysjobs WHERE name LIKE ''%SQLUndercover Periodic Backups Collection%'') IF @JobID IS NOT NULL BEGIN EXEC msdb.dbo.sp_update_jobstep @job_id = @JobID, @step_id=1 ,@command=N''EXEC ['+@Databasename+'].[Inspector].[InspectorDataCollection] @ModuleConfig = ''''PeriodicBackupCheck'''';''; END SET @JobID = NULL SET @JobID = (SELECT job_id FROM msdb.dbo.sysjobs WHERE name LIKE ''%SQLUndercover Inspector Report%'') IF @JobID IS NOT NULL BEGIN EXEC msdb.dbo.sp_update_job @job_id = @JobID, @description=N''Produce SQLUndercover Inspector HTML report from the collected data in ['+@Databasename+']'' END SET @JobID = NULL SET @JobID = (SELECT job_id FROM msdb.dbo.sysjobs WHERE name LIKE ''%SQLUndercover Periodic Backups Report%'') IF @JobID IS NOT NULL BEGIN EXEC msdb.dbo.sp_update_job @job_id = @JobID, @description=N''SQLUndercover Periodic Backup Report, check Backup information inserted into: ['+@Databasename+'] and report.'' END' EXEC (@SQLStatement); --Only transfer job schedules when you are upgrading from a build less than 2.00 IF (@CurrentBuild < '2.00') BEGIN RAISERROR('Transferring agent job shedules to the Modules and ModuleConfig tables',0,0) WITH NOWAIT; --Transfer schedules for existing Agent jobs and disable the existing Agent jobs. SELECT @SQLStatement = ( SELECT 'UPDATE [Inspector].['+[Tablename]+'] SET Frequency = '+CASE WHEN [Frequency] > 1440 THEN CAST(1440 AS VARCHAR(4)) ELSE CAST([Frequency] AS VARCHAR(4)) END+', StartTime = '''+CAST([StartTime] AS VARCHAR(8))+''', EndTime = '''+CAST([EndTime] AS VARCHAR(8))+''', IsActive = '+CAST([enabled] AS CHAR(1))+' WHERE ModuleConfig_Desc = '''+ModuleConfig_Desc+'''; EXEC msdb.dbo.sp_update_job @job_name = '''+Jobname+''',@enabled = 0; ' FROM ( --Get Schedule details for the Data collection and report jobs if there is one that is enabled for this database SELECT TOP 1 'Default' AS ModuleConfig_Desc, 'Modules' AS Tablename, Jobs.[name] AS Jobname, Jobs.[enabled], CASE [freq_subday_type] WHEN 1 THEN 1440 WHEN 2 THEN [freq_subday_interval]/60 WHEN 4 THEN [freq_subday_interval] WHEN 8 THEN [freq_subday_interval]*60 END [Frequency], CAST(msdb.dbo.agent_datetime(active_start_date,active_start_time) AS TIME(0)) AS StartTime, CAST(msdb.dbo.agent_datetime(active_end_date,active_end_time) AS TIME(0)) AS EndTime FROM msdb.dbo.sysjobs Jobs INNER JOIN msdb.dbo.sysjobsteps Steps ON Jobs.job_id = Steps.job_id INNER JOIN msdb.dbo.sysjobschedules JobSchedules ON JobSchedules.job_id = Jobs.job_id INNER JOIN msdb.dbo.sysschedules Schedules ON Schedules.schedule_id = JobSchedules.schedule_id WHERE Schedules.[enabled] = 1 AND Jobs.[name] LIKE '%SQLUndercover Inspector Data Collection%' AND (Steps.[command] LIKE '%'+@Databasename+'%' OR Steps.[database_name] = @Databasename) UNION SELECT TOP 1 'Default' AS ModuleConfig_Desc, 'ModuleConfig' AS Tablename, Jobs.[name] AS Jobname, Jobs.[enabled], CASE [freq_subday_type] WHEN 1 THEN 1440 WHEN 2 THEN [freq_subday_interval]/60 WHEN 4 THEN [freq_subday_interval] WHEN 8 THEN [freq_subday_interval]*60 END [Frequency], CAST(msdb.dbo.agent_datetime(active_start_date,active_start_time) AS TIME(0)), CAST(msdb.dbo.agent_datetime(active_end_date,active_end_time) AS TIME(0)) FROM msdb.dbo.sysjobs Jobs INNER JOIN msdb.dbo.sysjobsteps Steps ON Jobs.job_id = Steps.job_id INNER JOIN msdb.dbo.sysjobschedules JobSchedules ON JobSchedules.job_id = Jobs.job_id INNER JOIN msdb.dbo.sysschedules Schedules ON Schedules.schedule_id = JobSchedules.schedule_id WHERE Schedules.[enabled] = 1 AND Jobs.[name] LIKE '%SQLUndercover Inspector Report%' AND (Steps.[command] LIKE '%'+@Databasename+'%' OR Steps.[database_name] = @Databasename) UNION SELECT TOP 1 'PeriodicBackupCheck' AS ModuleConfig_Desc, 'Modules' AS Tablename, Jobs.[name] AS Jobname, Jobs.[enabled], CASE [freq_subday_type] WHEN 1 THEN 1440 WHEN 2 THEN [freq_subday_interval]/60 WHEN 4 THEN [freq_subday_interval] WHEN 8 THEN [freq_subday_interval]*60 END [Frequency], CAST(msdb.dbo.agent_datetime(active_start_date,active_start_time) AS TIME(0)), CAST(msdb.dbo.agent_datetime(active_end_date,active_end_time) AS TIME(0)) FROM msdb.dbo.sysjobs Jobs INNER JOIN msdb.dbo.sysjobsteps Steps ON Jobs.job_id = Steps.job_id INNER JOIN msdb.dbo.sysjobschedules JobSchedules ON JobSchedules.job_id = Jobs.job_id INNER JOIN msdb.dbo.sysschedules Schedules ON Schedules.schedule_id = JobSchedules.schedule_id WHERE Schedules.[enabled] = 1 AND Jobs.[name] LIKE '%SQLUndercover Periodic Backups Collection%' AND (Steps.[command] LIKE '%'+@Databasename+'%' OR Steps.[database_name] = @Databasename) UNION SELECT TOP 1 'PeriodicBackupCheck' AS ModuleConfig_Desc, 'ModuleConfig' AS Tablename, Jobs.[name] AS Jobname, Jobs.[enabled], CASE [freq_subday_type] WHEN 1 THEN 1440 WHEN 2 THEN [freq_subday_interval]/60 WHEN 4 THEN [freq_subday_interval] WHEN 8 THEN [freq_subday_interval]*60 END [Frequency], CAST(msdb.dbo.agent_datetime(active_start_date,active_start_time) AS TIME(0)), CAST(msdb.dbo.agent_datetime(active_end_date,active_end_time) AS TIME(0)) FROM msdb.dbo.sysjobs Jobs INNER JOIN msdb.dbo.sysjobsteps Steps ON Jobs.job_id = Steps.job_id INNER JOIN msdb.dbo.sysjobschedules JobSchedules ON JobSchedules.job_id = Jobs.job_id INNER JOIN msdb.dbo.sysschedules Schedules ON Schedules.schedule_id = JobSchedules.schedule_id WHERE Schedules.[enabled] = 1 AND Jobs.[name] LIKE '%SQLUndercover Periodic Backups Report%' AND (Steps.[command] LIKE '%'+@Databasename+'%' OR Steps.[database_name] = @Databasename) ) AgentJobs FOR XML PATH('') ,TYPE).value('.','nvarchar(max)'); EXEC(@SQLStatement); END EXEC sp_executesql N' --Update any Tablenames in PSConfig for Report only modules issue #204 , PopulatePSConfig proc also updated to accomodate. UPDATE PSConfig SET [Tablename] = NULL,[Procedurename] = [CollectionProcedurename] FROM [Inspector].[PSConfig] INNER JOIN [Inspector].[Modules] ON [PSConfig].[ModuleConfig_Desc] = [Modules].[ModuleConfig_Desc] AND [PSConfig].[Modulename] = [Modules].[Modulename] WHERE [Modules].[CollectionProcedurename] IS NULL AND [PSConfig].[Tablename] IS NOT NULL;'; --Set Frequency based Insert action as the default EXEC sp_executesql N' UPDATE [Inspector].[PSConfig] SET [InsertAction] = REPLACE(REPLACE([InsertAction],''1'',''3''),''2'',''3'');'; --Update Inspector Build UPDATE [Inspector].[Settings] SET [Value] = @Build WHERE [Description] = 'InspectorBuild' AND ([Value] != @Build OR [Value] IS NULL); --Log Upgrade/Installation in Upgrade history table EXEC sp_executesql N' INSERT INTO [Inspector].[InspectorUpgradeHistory] ([Log_Date], [PreserveData], [CurrentBuild], [TargetBuild], [RevisionDate], [SetupCommand]) VALUES (GETDATE(),CASE WHEN @InitialSetup = 0 THEN 1 ELSE 0 END,CAST(@CurrentBuild AS DECIMAL(4,2)),CAST(@Build AS DECIMAL(4,2)),@Revisiondate, ''EXEC [Inspector].[InspectorSetup] --Required Parameters (No defaults) @Databasename = ''''''+@Databasename+'''''', @DataDrive = ''''''+@DataDrive+'''''', @LogDrive = ''''''+@LogDrive+'''''', --Optional Parameters (Defaults Specified), ignored when @InitialSetup = 0 @BackupsPath = ''+ISNULL(''''''''+@BackupsPath+'''''''',''NULL'')+'', @StackNameForEmailSubject = ''+ISNULL(''''''''+@StackNameForEmailSubject+'''''''',''SQLUndercover'')+'', @EmailRecipientList = ''+ISNULL(''''''''+@EmailRecipientList+'''''''',''NULL'')+'', @DriveSpaceHistoryRetentionInDays = ''+CAST(ISNULL(@DriveSpaceHistoryRetentionInDays,90) AS VARCHAR(6))+'', @DaysUntilDriveFullThreshold = ''+CAST(ISNULL(@DaysUntilDriveFullThreshold,56) AS VARCHAR(6))+'', @FreeSpaceRemainingPercent = ''+CAST(ISNULL(@FreeSpaceRemainingPercent,10) AS VARCHAR(6))+'', @DriveLetterExcludes = ''+ISNULL(''''''''+@DriveLetterExcludes+'''''''',''NULL'')+'', @DatabaseGrowthsAllowedPerDay = ''+CAST(ISNULL(@DatabaseGrowthsAllowedPerDay,1) AS VARCHAR(6))+'', @MAXDatabaseGrowthsAllowedPerDay = ''+CAST(ISNULL(@MAXDatabaseGrowthsAllowedPerDay,10) AS VARCHAR(6))+'', @AgentJobOwnerExclusions = ''+ISNULL(''''''''+@AgentJobOwnerExclusions+'''''''',''''''sa'''''')+'', @FullBackupThreshold = ''+CAST(ISNULL(@FullBackupThreshold,8) AS VARCHAR(6))+'', @DiffBackupThreshold = ''+CAST(ISNULL(@DiffBackupThreshold,24) AS VARCHAR(6))+'', @LogBackupThreshold = ''+CAST(ISNULL(@LogBackupThreshold,20) AS VARCHAR(6))+'', @DatabaseOwnerExclusions = ''+ISNULL(''''''''+@DatabaseOwnerExclusions+'''''''',''''''sa'''''')+'', @LongRunningTransactionThreshold = ''+CAST(ISNULL(@LongRunningTransactionThreshold,300) AS VARCHAR(6))+'', @InitialSetup = ''+CAST(ISNULL(@InitialSetup,0) AS VARCHAR(1))+'', @EnableAgentJob = ''+CAST(ISNULL(@EnableAgentJob,0) AS VARCHAR(1))+'', @Help = ''+CAST(ISNULL(@Help,''NULL'') AS VARCHAR(1))+'';'' );', N'@Build DECIMAL(4,2), @CurrentBuild DECIMAL(4,2), @Databasename NVARCHAR(128), @DataDrive VARCHAR(50), @LogDrive VARCHAR(50), @BackupsPath VARCHAR(255), @StackNameForEmailSubject VARCHAR(255), @EmailRecipientList VARCHAR(1000), @DriveSpaceHistoryRetentionInDays INT, @DaysUntilDriveFullThreshold TINYINT, @FreeSpaceRemainingPercent TINYINT, @DriveLetterExcludes VARCHAR(10), @DatabaseGrowthsAllowedPerDay TINYINT, @MAXDatabaseGrowthsAllowedPerDay TINYINT, @AgentJobOwnerExclusions VARCHAR(255), @FullBackupThreshold TINYINT, @DiffBackupThreshold TINYINT, @LogBackupThreshold TINYINT, @DatabaseOwnerExclusions VARCHAR(255), @LongRunningTransactionThreshold INT, @InitialSetup BIT, @EnableAgentJob BIT, @Revisiondate DATE, @Help BIT', @Build = @Build, @CurrentBuild = @CurrentBuild, @Databasename = @Databasename, @DataDrive = @DataDrive, @LogDrive = @LogDrive, @BackupsPath = @BackupsPath, @StackNameForEmailSubject = @StackNameForEmailSubject, @EmailRecipientList = @EmailRecipientList, @DriveSpaceHistoryRetentionInDays = @DriveSpaceHistoryRetentionInDays, @DaysUntilDriveFullThreshold = @DaysUntilDriveFullThreshold, @FreeSpaceRemainingPercent = @FreeSpaceRemainingPercent, @DriveLetterExcludes = @DriveLetterExcludes, @DatabaseGrowthsAllowedPerDay = @DatabaseGrowthsAllowedPerDay, @MAXDatabaseGrowthsAllowedPerDay = @MAXDatabaseGrowthsAllowedPerDay, @AgentJobOwnerExclusions = @AgentJobOwnerExclusions, @FullBackupThreshold = @FullBackupThreshold, @DiffBackupThreshold = @DiffBackupThreshold, @LogBackupThreshold = @LogBackupThreshold, @DatabaseOwnerExclusions = @DatabaseOwnerExclusions, @LongRunningTransactionThreshold = @LongRunningTransactionThreshold, @InitialSetup = @InitialSetup, @EnableAgentJob = @EnableAgentJob, @Revisiondate = @Revisiondate, @Help = @Help; --Inspector Information PRINT ' =================================================================================================================================== Our Getting started guide can be found here: https://sqlundercover.com/2018/02/19/getting-started-with-the-sqlundercover-inspector/ =================================================================================================================================== ' PRINT ' ==================================================================== Be sure to check the following settings prior to using the solution: ==================================================================== [Inspector].[CurrentServers] - Ensure that ALL servers that you want to report on are here, the data needs to stored within this database so be sure to setup a centraliation solution if storing more than just this servers data. [Inspector].[EmailRecipients] - Default group is ''DBA'' set the Recipients column to a recipient email address or addesses seperated with a comma i.e Email1@Email.com,Email2@Email.com [Inspector].[Settings] - DriveSpaceRetentionPeriodInDays - Days of Drive space information to retain in the table [Inspector].[DriveSpace] - DatabaseGrowthsAllowedPerDay - Total Database Growths acceptable for a 24hour period (Yellow Advisory Condition) - MAXDatabaseGrowthsAllowedPerDay - MAX Database Growths for a 24hour period when you want to start seeing Red Warnings - DatabaseOwnerExclusions - Set Server login names that you want excluded from the database ownership check. - AgentJobOwnerExclusions - Set Server login names that you want excluded from the Agent job ownership check. - BackupsPath - Specify the backup path here for backup server free space vs estimated size required estimates (Requires Xp_cmdshell enabled) [Inspector].[Modules] - By Default all modules are enabled but you can control which ones you would like to have enabled, you can also add a new Row for customized configurations e.g different modules per server. ___________________________________________________________________________________________________________________________________________________________________________________________ ' END ELSE BEGIN RAISERROR('Please double check your database context, this script needs to be executed against the database [%s]',11,0,@Databasename) WITH NOWAIT; END END ELSE BEGIN RAISERROR ('@Datadrive and @LogDrive cannot be NULL or Blank',11,0) WITH NOWAIT; END END ELSE BEGIN RAISERROR('fn_SplitString does not exist, SQLUndercover Inspector requires fn_SplitString because your system is not compatible with STRING_SPLIT. Download fn_SplitString here - http://bit.ly/fn_SplitString and create the Function in the Master Database',0,0) END END GO DECLARE @DBname NVARCHAR(128) = DB_NAME(); RAISERROR(' --Inspector setup stored procedure is now available to run, below is an example call to the procedure. EXEC [%s].[Inspector].[InspectorSetup] --Required Parameters (No defaults) @Databasename = ''%s'', @DataDrive = ''S,U'', @LogDrive = ''T,V'', --Optional Parameters (Defaults Specified), ignored when @InitialSetup = 0 @BackupsPath = ''F:\Backups'', @StackNameForEmailSubject = ''SQLUndercover'', @EmailRecipientList = NULL, @DriveSpaceHistoryRetentionInDays = 90, @DaysUntilDriveFullThreshold = 56, @FreeSpaceRemainingPercent = 10, @DriveLetterExcludes = NULL, @DatabaseGrowthsAllowedPerDay = 1, @MAXDatabaseGrowthsAllowedPerDay = 10, @AgentJobOwnerExclusions = ''sa'', @FullBackupThreshold = 8, @DiffBackupThreshold = 24, @LogBackupThreshold = 20, @DatabaseOwnerExclusions = ''sa'', @LongRunningTransactionThreshold = 300, @StartTime = ''08:55'', @EndTime = ''17:30'', @EnableAgentJob = 1, @InitialSetup = 0; ',0,0,@DBname,@DBname) WITH NOWAIT; /* EXEC sp_executesql N' EXEC [Inspector].[InspectorSetup] @Databasename = @DBname, @DataDrive = ''S,U'', @LogDrive = ''T,V'';', N'@DBname NVARCHAR(128)', @DBname =@DBname; */