From 5d867687bda1c27595e302f5f56e2c2d07644769 Mon Sep 17 00:00:00 2001 From: KammM Date: Wed, 25 Feb 2026 16:48:39 +0100 Subject: [PATCH] PRDD_MAINTAIN_DATABASES: Renamed --- .../[PRDD_MAINTAIN_DATABASES].sql | 1157 -------- .../[PRDD_MAINTAIN_DATABASE].sql | 2357 +++++++++++++++++ 2 files changed, 2357 insertions(+), 1157 deletions(-) delete mode 100644 current/[DD_SYS]-Database/[PRDD_MAINTAIN_DATABASES]/[PRDD_MAINTAIN_DATABASES].sql create mode 100644 current/[DD_SYS]-Database/[PRDD_MAINTAIN_DATABASE]/[PRDD_MAINTAIN_DATABASE].sql diff --git a/current/[DD_SYS]-Database/[PRDD_MAINTAIN_DATABASES]/[PRDD_MAINTAIN_DATABASES].sql b/current/[DD_SYS]-Database/[PRDD_MAINTAIN_DATABASES]/[PRDD_MAINTAIN_DATABASES].sql deleted file mode 100644 index eff2634..0000000 --- a/current/[DD_SYS]-Database/[PRDD_MAINTAIN_DATABASES]/[PRDD_MAINTAIN_DATABASES].sql +++ /dev/null @@ -1,1157 +0,0 @@ -USE [DD_SYS] -GO - -SET ANSI_NULLS ON -GO -SET QUOTED_IDENTIFIER ON -GO - --- [PRDD_MAINTAIN_DATABASES] --- ================================================================= --- Checks database health, shrinks all database and log files --- and recreates indizes in a maintanance run. --- Minimum requirement: MS SQL Server 2016 --- --- Returns: INT Value - 0 = Everything worked well --- ================================================================= --- Copyright (c) 2025 by Digital Data GmbH --- --- Digital Data GmbH • Ludwig-Rinn-Strasse 16 • D-35452 Heuchelheim --- Tel.: 0641/202360 • E-Mail: info-flow@digitaldata.works --- ================================================================= --- Creation Date / Author: 29.11.2024 / MK --- Version Date / Editor: 20.03.2025 / MK --- Version Number: 1.2.0.0 --- ================================================================= --- History: --- 29.11.2024 / MK - First Version --- 06.12.2024 / MK - Added "SET ONLINE" after forced SINGLE_USER Mode, Added repair parameter for DBCHECK, added @pRECOMPILEPROCEDURES, some minor error fixes --- 13.12.2024 / MK - New way the get the procedure name, failsafe for parameters implemented --- 20.03.2025 / MK - Improved safty checks - -CREATE OR ALTER PROCEDURE [dbo].[PRDD_MAINTAIN_DATABASES] ( - @pCHECKDB BIT = 1, -- Set to 1 to make a check of the database before shrinking incl. soft repair function. Otherwise set to 0. - @pSHRINKLOG BIT = 1, -- Set to 1 to shrink the log file. Otherwise set to 0. - @pSHRINKDB BIT = 0, -- Set to 1 to shrink the database file as well. Otherwise set to 0. (Dont use on FILESTREAM databases!) - @pREBUILDINDEX BIT = 0, -- Set to 1 to recreate all database indexes. Otherwise set to 0. - @pRECOMPILEPROCEDURES BIT = 0, -- Set to 1 to recompile all database procedures. Otherwise set to 0. - @pCLEARQUERYCACHE BIT = 0, -- Set to 1 to clear the QL query cache via DROPCLEANBUFFERS. Otherwise set to 0. - @pFORCE BIT = 0, -- Set to 1 to force the shrinking, it will cut all current sessens. In combination with @pCHECKDB it trys the soft reapair dbs. Otherwise set to 0. - @pINCLUDEDB NVARCHAR(1000) = 'DD_ECM', -- Set a list of included databases. IF <> NULL, it will override the @pEXCLUDEDB Parameter - @pEXCLUDEDB NVARCHAR(1000) = 'master,model,msdb,tempdb', -- Set a list of exluded databases. Default exclusen are the system databases. - @pLOGLEVEL NVARCHAR(25) = 'ERROR' -- Set to 'INFO','WARN' OR 'ERROR' (or NULL to disable), logging to table: [TBDD_MAINTAIN_DATABASES_LOG] - -- 'INFO' includes Infos, Warnings and Errors, 'WARN' only warnings and Errors and 'ERROR' only Errors -) -AS -BEGIN - - SET NOCOUNT ON; - - -- declare new vars because of parameter sniffing - DECLARE @CHECKDB BIT = ISNULL(@pCHECKDB,1), - @SHRINKLOG BIT = ISNULL(@pSHRINKLOG,1), - @SHRINKDB BIT = ISNULL(@pSHRINKDB,0), - @REBUILDINDEX BIT = ISNULL(@pREBUILDINDEX,0), - @RECOMPILEPROCEDURES BIT = ISNULL(@pRECOMPILEPROCEDURES,0), - @CLEARQUERYCACHE BIT = ISNULL(@pCLEARQUERYCACHE,0), - @FORCE BIT = ISNULL(@pFORCE,0), - @INCLUDEDB NVARCHAR(1000) = LTRIM(RTRIM(ISNULL(@pINCLUDEDB,DB_NAME()))), - @EXCLUDEDB NVARCHAR(1000) = LTRIM(RTRIM(ISNULL(@pEXCLUDEDB,'master,model,msdb,tempdb'))), - @LOGLEVEL NVARCHAR(25) = LTRIM(RTRIM(ISNULL(@pLOGLEVEL,'ERROR'))); - - -- declare runtime vars - DECLARE @MyProcedureName NVARCHAR(128) = OBJECT_NAME(@@PROCID); - DECLARE @ProcedureName NVARCHAR(50), - @DBName NVARCHAR(100), - @DBNameCount INT, - @LogFileName NVARCHAR(100), - @CurrentLogFileSizeInMB INT, - @MinimumLogFileSizeInMB INT, - @MaximumLogFileSizeInMB INT, - @TargetLogFileSizeInMB INT, - @ProductVersion sql_variant, - @ProductMainVersion INT, - @ProductLevel sql_variant, - @ProductEdition sql_variant, - @SchemaName NVARCHAR(50), - @TableName NVARCHAR(256), - @LoginName NVARCHAR(50), - @HostName NVARCHAR(50), - @SessionID NVARCHAR(50) = NULL, - @MySessionID NVARCHAR(50) = @@SPID, - @SQLCommand NVARCHAR(MAX) = NULL, - @TableListCount INT = 0, - @ProcedureListCount INT = 0, - @return_status NVARCHAR(50) = 0, - @return_status_text NVARCHAR(MAX) = 'START ' + @MyProcedureName + ' @ ' + CONVERT(varchar(50),GETDATE(),120); - - PRINT '===============================' - PRINT 'PROCEDURE - ' + @return_status_text; - PRINT 'PARAMETER01 - @CHECKDB: ' + CONVERT(VARCHAR(1),@CHECKDB); - PRINT 'PARAMETER02 - @SHRINKLOG: ' + CONVERT(VARCHAR(1),@SHRINKLOG); - PRINT 'PARAMETER03 - @SHRINKDB: ' + CONVERT(VARCHAR(1),@SHRINKDB); - PRINT 'PARAMETER04 - @REBUILDINDEX: ' + CONVERT(VARCHAR(1),@REBUILDINDEX); - PRINT 'PARAMETER05 - @RECOMPILEPROCEDURES: ' + CONVERT(VARCHAR(1),@RECOMPILEPROCEDURES); - PRINT 'PARAMETER06 - @CLEARQUERYCACHE: ' + CONVERT(VARCHAR(1),@CLEARQUERYCACHE); - PRINT 'PARAMETER07 - @FORCE: ' + CONVERT(VARCHAR(1),@FORCE); - PRINT 'PARAMETER08 - @INCLUDEDB: ' + CONVERT(VARCHAR(1000),@INCLUDEDB); - PRINT 'PARAMETER09 - @EXCLUDEDB: ' + CONVERT(VARCHAR(1000),@EXCLUDEDB); - PRINT 'PARAMETER10 - @LOGLEVEL: ' + CONVERT(VARCHAR(25),@LOGLEVEL); - - --=================================================-- Get server infos --==================================================-- - SELECT @ProductVersion = SERVERPROPERTY('productversion'), @ProductLevel = SERVERPROPERTY ('productlevel'), @ProductEdition = SERVERPROPERTY ('edition'); - SET @ProductMainVersion = ISNULL(LEFT(convert(VARCHAR(100),@ProductVersion), CHARINDEX('.', convert(VARCHAR(100),@ProductVersion)) - 1),0); - - PRINT ''; - PRINT 'Informations about this Server:'; - PRINT '@MySessionID: ' + CONVERT(VARCHAR(100),@MySessionID); - PRINT '@ProductVersion: ' + CONVERT(VARCHAR(100),@ProductVersion); - PRINT '@ProductMainVersion: ' + CONVERT(VARCHAR(100),@ProductMainVersion); - PRINT '@ProductLevel: ' + CONVERT(VARCHAR(100),@ProductLevel); - PRINT '@ProductEdition: ' + CONVERT(VARCHAR(100),@ProductEdition); - ----------------------------------------------------------------------------------------------------------------------------- - - --==============================================-- Prepare the log table --================================================-- - IF (@LOGLEVEL is not NULL) BEGIN - PRINT ''; - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - PRINT 'Log table already exists'; - END; ELSE BEGIN - PRINT 'Log table does not exists, trying to create...'; - - CREATE TABLE [dbo].[TBDD_MAINTAIN_DATABASES_LOG]( - [GUID] [bigint] IDENTITY(1,1) NOT NULL, - [LOG_LEVEL] [varchar](25) NOT NULL, - [MESSAGE1] [varchar](max) NOT NULL, - [MESSAGE2] [varchar](max) NULL, - [MESSAGE3] [varchar](max) NULL, - [MESSAGE4] [varchar](max) NULL, - [MESSAGE5] [varchar](max) NULL, - [COMMENT] [varchar](max) NULL, - [ADDED_WHO] [varchar](50) NOT NULL, - [ADDED_WHEN] [datetime] NOT NULL, - CONSTRAINT [PK_TBDD_MAINTAIN_DATABASES_LOG] PRIMARY KEY CLUSTERED - ( - [GUID] ASC - )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] - ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]; - - ALTER TABLE [dbo].[TBDD_MAINTAIN_DATABASES_LOG] ADD CONSTRAINT [DF_TBDD_MAINTAIN_DATABASES_LOG_ADDED_WHO] DEFAULT ('DEFAULT') FOR [ADDED_WHO]; - - ALTER TABLE [dbo].[TBDD_MAINTAIN_DATABASES_LOG] ADD CONSTRAINT [DF_TBDD_MAINTAIN_DATABASES_LOG_ADDED_WHEN] DEFAULT (getdate()) FOR [ADDED_WHEN]; - - END; - END; ELSE BEGIN - PRINT ''; - PRINT 'Skipping Logging to log table!'; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log start to table --=================================================-- - IF ((@LOGLEVEL in ('INFO')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL,'preparing', 'procedure', @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --=============================================-- Get the dbs for the loop --==============================================-- - -- Create a temporary table to hold the table names - CREATE TABLE #DBList (DBName NVARCHAR(256)); - - IF (@ProductMainVersion >= 13) BEGIN - - IF (@INCLUDEDB is not NULL) BEGIN - INSERT INTO #DBList(DBName) - SELECT [name] as 'DBName' - FROM [master].[sys].[databases] (NOLOCK) - WHERE [name] IN (SELECT [value] FROM STRING_SPLIT(@INCLUDEDB, ',')) -- include these databases - AND [state] = 0 -- database is online - AND [is_in_standby] = 0; -- database is not read only for log shipping - END; ELSE BEGIN - INSERT INTO #DBList(DBName) - SELECT [name] as 'DBName' - FROM [master].[sys].[databases] (NOLOCK) - WHERE [name] NOT IN (SELECT [value] FROM STRING_SPLIT(@EXCLUDEDB, ',')) -- exclude these databases - AND [state] = 0 -- database is online - AND [is_in_standby] = 0; -- database is not read only for log shipping - END; - - END; ELSE BEGIN - - PRINT 'Because of the SQL Version only one DB can be processed!' - INSERT INTO #DBList(DBName) - SELECT [name] as 'DBName' - FROM [master].[sys].[databases] (NOLOCK) - WHERE [name] = @INCLUDEDB -- use only this database - AND [state] = 0 -- database is online - AND [is_in_standby] = 0; -- database is not read only for log shipping - - END; - - SELECT @DBNameCount = COUNT(*) FROM #DBList; - ----------------------------------------------------------------------------------------------------------------------------- - - SET @return_status_text = 'Found ' + convert(VARCHAR,@DBNameCount) + ' database(s) to process!'; - PRINT @return_status_text; - - --===============================================-- Log result to table --=================================================-- - IF (@LOGLEVEL in ('INFO','WARN')) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_list', 'count', @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --==========================================-- Create the main loop (cursor) --============================================-- - IF (@DBNameCount > 0) BEGIN - - DECLARE db_cursor CURSOR STATIC LOCAL FOR - SELECT [DBName] as 'DBName' FROM #DBList; - - OPEN db_cursor - FETCH NEXT FROM db_cursor INTO @DBName - WHILE @@FETCH_STATUS = 0 BEGIN - BEGIN TRY - - PRINT ' '; - PRINT '-------------------------------'; - PRINT 'Now processing: ' + CONVERT(varchar(100),@DBName); - - --==============================================-- Change db working mode --===============================================-- - IF (@FORCE = 1) and (@return_status = 0) BEGIN - - PRINT 'Closing active database connections... first time'; - DECLARE kill_cursor CURSOR STATIC LOCAL FOR - SELECT CONVERT(NVARCHAR(5), [session_id]) as 'SessionID', - CONVERT(NVARCHAR(50), [login_name]) as 'LoginName', - CONVERT(NVARCHAR(50), [host_name]) as 'HostName' - FROM [master].[sys].[dm_exec_sessions] (NOLOCK) - WHERE [is_user_process] = 1 - AND [database_id] = db_id(@DBName) - AND [session_id] <> @MySessionID; - - OPEN kill_cursor; - FETCH NEXT FROM kill_cursor INTO @SessionID, @LoginName, @HostName; - WHILE @@FETCH_STATUS = 0 BEGIN - - BEGIN TRY - SET @SQLCommand = 'KILL ' + @SessionID + ';'; - PRINT 'Killing @SessionID: ' + @SessionID + ', @LoginName: ' + @LoginName + ', @HostName: ' + @HostName; - EXEC sp_executesql @SQLCommand; - SET @SQLCommand = NULL; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Closing connections to database was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Closing connections to database returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'kill_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - END TRY - BEGIN CATCH - - -- Handle any errors that occur during the process. - SELECT ERROR_MESSAGE() AS ErrorMessage; - - SET @SQLCommand = NULL; - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL is not null) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) - VALUES ('ERROR', 'kill_cursor', @DBName, ERROR_MESSAGE(),@MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - END CATCH; - - FETCH NEXT FROM kill_cursor INTO @SessionID, @LoginName, @HostName; - - END; - - CLOSE kill_cursor; - DEALLOCATE kill_cursor; - ----------------------------------------------------------------------------------------------------------------------------- - - PRINT 'Set the database to SINGLE_USER mode to avoid multiple active sessions.'; - SET @SQLCommand = N'ALTER DATABASE [' + @DBName + '] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;'; - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Setting the database to SINGLE_USER mode was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Setting the database to SINGLE_USER mode returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - END; ELSE BEGIN - - IF (@LOGLEVEL in ('INFO')) BEGIN - SET @return_status_text = 'Skipping to change the database mode (FORCE = OFF)!'; - PRINT @return_status_text; - END; - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Check db consistency --================================================-- - IF (@CHECKDB = 1) and (@return_status = 0) BEGIN - - IF (@FORCE = 1) BEGIN - PRINT 'Check the database for consistency and health and try soft repair.'; - SET @SQLCommand = N'USE [' + @DBName + ']; - ALTER DATABASE [' + @DBName + '] SET RECOVERY SIMPLE; - DBCC CHECKDB ([' + @DBName + '],REPAIR_REBUILD) WITH NO_INFOMSGS, ALL_ERRORMSGS; - ALTER DATABASE [' + @DBName + '] SET RECOVERY FULL;'; - END; ELSE BEGIN - PRINT 'Check the database for consistency and health.'; - SET @SQLCommand = N'USE [' + @DBName + ']; - ALTER DATABASE [' + @DBName + '] SET RECOVERY SIMPLE; - DBCC CHECKDB ([' + @DBName + ']) WITH NO_INFOMSGS, ALL_ERRORMSGS; - ALTER DATABASE [' + @DBName + '] SET RECOVERY FULL;'; - END; - - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Check the database for consistency and health was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Check the database for consistency and health returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - END; ELSE BEGIN - IF (@LOGLEVEL in ('INFO')) BEGIN - SET @return_status_text = 'Skipping the database check for consistency and health!'; - PRINT @return_status_text; - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --============================================-- Prepare shrinking the log--===============================================-- - IF (@SHRINKLOG = 1) BEGIN - - --===========================================-- Get infos about the log file --============================================-- - PRINT 'Get the current and minimum size of the log file in MB.'; - SET @SQLCommand = N'SELECT TOP 1 @LogFileName = [name], @CurrentLogFileSizeInMB = ([size] / 128), @MinimumLogFileSizeInMB = ([growth] / 128), @MaximumLogFileSizeInMB = ([max_size] / 128) - FROM [master].[sys].[master_files] (NOLOCK) - WHERE [database_id] = DB_ID(''' + @DBName + ''') AND [STATE_DESC] = ''ONLINE'' AND [type_desc] = ''LOG'''; - EXEC @return_status = sp_executesql @SQLCommand, N'@LogFileName VARCHAR(100) OUTPUT, @CurrentLogFileSizeInMB INT OUTPUT, @MinimumLogFileSizeInMB INT OUTPUT, @MaximumLogFileSizeInMB INT OUTPUT', - @LogFileName OUTPUT, @CurrentLogFileSizeInMB OUTPUT, @MinimumLogFileSizeInMB OUTPUT, @MaximumLogFileSizeInMB OUTPUT; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Getting log file settings was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Getting log file settings returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===========================================-- Calculate target file size --==============================================-- - PRINT 'The @CurrentLogFileSizeInMB is: ' + CONVERT(VARCHAR(1000),@CurrentLogFileSizeInMB); - PRINT 'The @MinimumLogFileSizeInMB is: ' + CONVERT(VARCHAR(1000),@MinimumLogFileSizeInMB); - PRINT 'The @MaximumLogFileSizeInMB is: ' + CONVERT(VARCHAR(1000),@MaximumLogFileSizeInMB); - - SET @TargetLogFileSizeInMB = CASE WHEN (@MinimumLogFileSizeInMB < @CurrentLogFileSizeInMB) AND (@MinimumLogFileSizeInMB < @MaximumLogFileSizeInMB) - THEN @MinimumLogFileSizeInMB - ELSE @CurrentLogFileSizeInMB - END; - - PRINT 'The @TargetLogFileSizeInMB is: ' + CONVERT(VARCHAR(1000),@TargetLogFileSizeInMB); - ----------------------------------------------------------------------------------------------------------------------------- - - --=================================================-- Schrink db logs --===================================================-- - IF (@CurrentLogFileSizeInMB > @TargetLogFileSizeInMB) BEGIN - - --=============================================-- Change db recovery mode --===============================================-- - PRINT 'Truncating the log needs to change the database recovery model to SIMPLE.'; - SET @SQLCommand = N'ALTER DATABASE [' + @DBName + '] SET RECOVERY SIMPLE'; - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Setting database to the simple recovery model was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Setting database to the simple recovery model returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===================================================-- Now, do it! --=====================================================-- - IF (@return_status = 0) BEGIN - PRINT 'Shrink the truncated log file to 10% of its current size or the minimum size, whichever is larger.'; - IF (@ProductMainVersion >= 16) BEGIN - IF (@FORCE = 1) BEGIN - SET @SQLCommand = N'USE [' + @DBName + ']; DBCC SHRINKFILE (''' + @LogFileName + ''', ' + CAST(@TargetLogFileSizeInMB AS VARCHAR) + ') WITH WAIT_AT_LOW_PRIORITY (ABORT_AFTER_WAIT = BLOCKERS), NO_INFOMSGS;'; - END; ELSE BEGIN - SET @SQLCommand = N'USE [' + @DBName + ']; DBCC SHRINKFILE (''' + @LogFileName + ''', ' + CAST(@TargetLogFileSizeInMB AS VARCHAR) + ') WITH WAIT_AT_LOW_PRIORITY (ABORT_AFTER_WAIT = SELF), NO_INFOMSGS;'; - END; - END; ELSE BEGIN - PRINT 'Because of the sql server version, DBCC SHRINKFILE is not applicable with the FORCE parameter!'; - SET @SQLCommand = N'USE [' + @DBName + ']; DBCC SHRINKFILE (''' + @LogFileName + ''', ' + CAST(@TargetLogFileSizeInMB AS VARCHAR) + ') WITH NO_INFOMSGS;'; - END; - - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Shrinking the database log file was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Shrinking the database log file returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - END; ELSE BEGIN - SET @return_status_text = 'Shrinking the database log was skipped because of previouse error(s)!'; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --=============================================-- Change db recovery mode --===============================================-- - PRINT 'Reset the database recovery model.' - SET @SQLCommand = N'ALTER DATABASE [' + @DBName + '] SET RECOVERY FULL'; - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Setting database back to the full recovery model was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Setting database back to the full recovery model returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - END; ELSE BEGIN - PRINT 'No shrink action neccessary!'; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - END; ELSE BEGIN - PRINT 'Skipping to shrink the log file!'; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --================================================-- Shrink db as well --==================================================-- - IF ((@SHRINKDB = 1) and (@return_status = 0)) BEGIN - PRINT 'Shrink the database to reclaim unused space.'; - IF (@ProductMainVersion >= 16) BEGIN - IF (@FORCE = 1) BEGIN - SET @SQLCommand = N'USE [' + @DBName + ']; DBCC SHRINKDATABASE ([' + @DBName + ']) WITH WAIT_AT_LOW_PRIORITY (ABORT_AFTER_WAIT = BLOCKERS), NO_INFOMSGS;'; - END; ELSE BEGIN - SET @SQLCommand = N'USE [' + @DBName + ']; DBCC SHRINKDATABASE ([' + @DBName + ']) WITH WAIT_AT_LOW_PRIORITY (ABORT_AFTER_WAIT = SELF), NO_INFOMSGS;'; - END; - END; ELSE BEGIN - PRINT 'Because of the sql server version, DBCC SHRINKDATABASE is not applicable with the FORCE parameter!'; - SET @SQLCommand = N'USE [' + @DBName + ']; DBCC SHRINKDATABASE ([' + @DBName + ']) WITH NO_INFOMSGS;'; - END; - - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Shrinking the database was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Shrinking the database returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - END; ELSE BEGIN - IF (@LOGLEVEL in ('INFO')) BEGIN - SET @return_status_text = 'Skipping shrinking the database!'; - PRINT @return_status_text; - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --=============================================-- Rebuild database indexes --==============================================-- - IF ((@REBUILDINDEX = 1) and (@return_status = 0)) BEGIN - PRINT 'Prepare to rebuild database indexes.'; - - BEGIN TRY - DROP TABLE #TableList; - END TRY - BEGIN CATCH - PRINT '#TableList Temp table is already gone... (1)'; - END CATCH; - - -- Create a temporary table to hold the table names - CREATE TABLE #TableList (SchemaName NVARCHAR(50), TableName NVARCHAR(256)); - - -- Set the context to the specified database and fill the temporary table - SET @SQLCommand = ' INSERT INTO #TableList (SchemaName, TableName) - SELECT DISTINCT (SCHEMA_NAME([schema_id])), t.[name] - FROM [' + @DBName + '].[sys].[tables] t - INNER JOIN [' + @DBName + '].[sys].[indexes] i ON t.[object_id] = i.[object_id] - WHERE i.[type] IN (1, 2) -- Clustered and Non-Clustered indexes;'; - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Getting the tables was successfully done!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Getting the tables returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - IF (@return_status = 0) BEGIN - SELECT @TableListCount = COUNT(*) FROM #TableList; - PRINT 'Found: ' + convert(VARCHAR,@TableListCount) + ' tables to reindex'; - END; ELSE BEGIN - SET @TableListCount = 0; - PRINT 'Found: no tables to reindex'; - END; - - END; ELSE BEGIN - IF (@LOGLEVEL in ('INFO')) BEGIN - SET @return_status_text = 'Skipping rebuilding database indexes!'; - PRINT @return_status_text; - END; - END; - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - IF ((@REBUILDINDEX = 1) and (@return_status = 0) and (@TableListCount > 0)) BEGIN - - DECLARE table_cursor CURSOR STATIC LOCAL FOR - SELECT SchemaName, TableName FROM #TableList; - - OPEN table_cursor; - FETCH NEXT FROM table_cursor INTO @SchemaName, @TableName; - WHILE @@FETCH_STATUS = 0 BEGIN - BEGIN TRY - - PRINT 'Rebuild Index for table: ' + CONVERT(VARCHAR(1000),@TableName); - SET @SQLCommand = 'ALTER INDEX ALL ON [' + @DBName + '].[' + @SchemaName + '].[' + @TableName + '] ' + - 'REBUILD WITH (FILLFACTOR = 80, SORT_IN_TEMPDB = ON, STATISTICS_NORECOMPUTE = ON)'; - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Recreation the database indexes of Table [' + @TableName + '] successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Recreation the database indexes of Table [' + @TableName + '] returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'table_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - -- Short break to avoid access violations - WAITFOR DELAY '00:00:03'; - - ----------------------------------------------------------------------------------------------------------------------------- - PRINT 'Closing active database connections... again'; - DECLARE kill_cursor CURSOR STATIC LOCAL FOR - SELECT CONVERT(NVARCHAR(5), [session_id]) as 'SessionID', - CONVERT(NVARCHAR(50), [login_name]) as 'LoginName', - CONVERT(NVARCHAR(50), [host_name]) as 'HostName' - FROM [master].[sys].[dm_exec_sessions] (NOLOCK) - WHERE [is_user_process] = 1 - AND [database_id] = db_id(@DBName) - AND [session_id] <> @MySessionID; - - OPEN kill_cursor; - FETCH NEXT FROM kill_cursor INTO @SessionID, @LoginName, @HostName; - WHILE @@FETCH_STATUS = 0 BEGIN - - BEGIN TRY - - SET @SQLCommand = 'KILL ' + @SessionID + ';'; - PRINT 'Killing @SessionID: ' + @SessionID + ', @LoginName: ' + @LoginName + ', @HostName: ' + @HostName; - EXEC sp_executesql @SQLCommand; - SET @SQLCommand = NULL; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Closing connections to database was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Closing connections to database returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'kill_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - END TRY - BEGIN CATCH - - -- Handle any errors that occur during the process. - SELECT ERROR_MESSAGE() AS ErrorMessage; - - SET @SQLCommand = NULL; - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL is not null) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) - VALUES ('ERROR', 'kill_cursor', @DBName, ERROR_MESSAGE(),@MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - END CATCH; - - FETCH NEXT FROM kill_cursor INTO @SessionID, @LoginName, @HostName; - - END; - - CLOSE kill_cursor; - DEALLOCATE kill_cursor; - ----------------------------------------------------------------------------------------------------------------------------- - - FETCH NEXT FROM table_cursor INTO @SchemaName, @TableName; - - END TRY - BEGIN CATCH - - -- Handle any errors that occur during the process. - SELECT ERROR_MESSAGE() AS ErrorMessage; - - --================================================-- Log error to table --=================================================-- - IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) - VALUES ('ERROR', 'table_cursor', @DBName, ERROR_MESSAGE(),@MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - FETCH NEXT FROM table_cursor INTO @SchemaName, @TableName; - - END CATCH; - - END; - - CLOSE table_cursor; - DEALLOCATE table_cursor; - - -- Drop the temporary table - DROP TABLE #TableList; - - SET @return_status_text = 'Recreation of the database indexes successfully completed!'; - PRINT @return_status_text; - - END; ELSE BEGIN - IF (@LOGLEVEL in ('INFO')) BEGIN - SET @return_status_text = 'Skipping the recreation of the database indexes!'; - PRINT @return_status_text; - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --=========================================-- Prepare to recompile Procedures --===========================================-- - IF ((@RECOMPILEPROCEDURES = 1) and (@return_status = 0)) BEGIN - PRINT 'Prepare to recompile database procedures.'; - - BEGIN TRY - DROP TABLE #ProcedureList; - END TRY - BEGIN CATCH - PRINT '#ProcedureList Temp table is already gone... (1)'; - END CATCH; - - -- Create a temporary table to hold the procedure names - CREATE TABLE #ProcedureList (SchemaName NVARCHAR(50), ProcedureName NVARCHAR(256)); - - -- Set the context to the specified database and fill the temporary table - SET @SQLCommand = ' INSERT INTO #ProcedureList (SchemaName, ProcedureName) - SELECT SPECIFIC_CATALOG, SPECIFIC_NAME - FROM [' + @DBName + '].INFORMATION_SCHEMA.ROUTINES (NOLOCK) - WHERE ROUTINE_TYPE = ''PROCEDURE'' - AND SPECIFIC_NAME <> ''' + @MyProcedureName + ''' - ORDER BY SPECIFIC_NAME;'; - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Getting the procedures was successfully done!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Getting the procedures returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - IF (@return_status = 0) BEGIN - SELECT @ProcedureListCount = COUNT(*) FROM #ProcedureList; - PRINT 'Found: ' + convert(VARCHAR,@ProcedureListCount) + ' procedures to recompile'; - END; ELSE BEGIN - SET @ProcedureListCount = 0; - PRINT 'Found: no procedures to recompile'; - END; - - END; ELSE BEGIN - IF (@LOGLEVEL in ('INFO')) BEGIN - SET @return_status_text = 'Skipping recompiling procedures!'; - PRINT @return_status_text; - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Recompile Procedures --================================================-- - IF ((@RECOMPILEPROCEDURES = 1) and (@return_status = 0) and (@ProcedureListCount > 0)) BEGIN - - PRINT 'Removing all procedure elements from the query plan cache.'; - SET @SQLCommand = N'USE [' + @DBName + ']; DBCC FREEPROCCACHE;'; - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Removing all procedure elements was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Removing all procedure elements returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'procedure_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - DECLARE procedure_cursor CURSOR STATIC LOCAL FOR - SELECT SchemaName, ProcedureName FROM #ProcedureList; - - OPEN procedure_cursor; - FETCH NEXT FROM procedure_cursor INTO @SchemaName, @ProcedureName; - WHILE @@FETCH_STATUS = 0 BEGIN - BEGIN TRY - - PRINT 'Recompile procedure: ' + CONVERT(VARCHAR(1000),@ProcedureName); - SET @SQLCommand = 'USE [' + @DBName + ']; EXEC sp_recompile ''' + @ProcedureName + '''; '; - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Recompiling the procedure [' + @ProcedureName + '] successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Recompiling the procedure [' + @ProcedureName + '] returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'procedure_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - FETCH NEXT FROM procedure_cursor INTO @SchemaName, @ProcedureName; - - END TRY - BEGIN CATCH - - -- Handle any errors that occur during the process. - SELECT ERROR_MESSAGE() AS ErrorMessage; - - --================================================-- Log error to table --=================================================-- - IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) - VALUES ('ERROR', 'procedure_cursor', @DBName, ERROR_MESSAGE(),@MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - FETCH NEXT FROM procedure_cursor INTO @SchemaName, @ProcedureName; - - END CATCH; - - END; - - CLOSE procedure_cursor; - DEALLOCATE procedure_cursor; - - -- Drop the temporary table - DROP TABLE #ProcedureList; - - SET @return_status_text = 'Recompiling of procedures successfully completed!'; - PRINT @return_status_text; - - END; ELSE BEGIN - IF (@LOGLEVEL in ('INFO')) BEGIN - SET @return_status_text = 'Skipping the recompiling of the procedures!'; - PRINT @return_status_text; - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'procedure_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --================================================-- Clear Query Cache --==================================================-- - - IF ((@CLEARQUERYCACHE = 1) and (@return_status = 0)) BEGIN - - PRINT 'Removing the sql query cache.'; - SET @SQLCommand = N'USE [' + @DBName + ']; DBCC DROPCLEANBUFFERS;'; - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Removing sql query cache was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Removing sql query cache returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - - END; ELSE BEGIN - IF (@LOGLEVEL in ('INFO')) BEGIN - SET @return_status_text = 'Skipping the query cache clearing!'; - PRINT @return_status_text; - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --==============================================-- Change db working mode --===============================================-- - IF (@FORCE = 1) BEGIN -- and ignore last result - PRINT 'Set the database back to MULTI_USER mode'; - SET @SQLCommand = N'ALTER DATABASE [' + @DBName + '] SET MULTI_USER; ALTER DATABASE [' + @DBName + '] SET ONLINE;'; - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Setting the database to MULTI_USER mode was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Setting the database to MULTI_USER mode returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - END; ELSE BEGIN - IF (@LOGLEVEL in ('INFO')) BEGIN - SET @return_status_text = 'Skipping to change the database mode (FORCE = OFF)!'; - PRINT @return_status_text; - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - -- Ensure temp table is droped and cursor is closed - BEGIN TRY - DROP TABLE #DBList; - END TRY - BEGIN CATCH - PRINT '#DBList Temp table are already gone...'; - END CATCH; - - IF (@REBUILDINDEX = 1) BEGIN - - BEGIN TRY - DROP TABLE #TableList; - END TRY - BEGIN CATCH - PRINT '#TableList Temp table are already gone...(1)'; - END CATCH; - - END; - - IF (@RECOMPILEPROCEDURES = 1) BEGIN - - BEGIN TRY - DROP TABLE #ProcedureList; - END TRY - BEGIN CATCH - PRINT '#ProcedureList Temp table are already gone...(1)'; - END CATCH; - - BEGIN TRY - CLOSE procedure_cursor; - DEALLOCATE procedure_cursor; - END TRY - BEGIN CATCH - PRINT 'Procedure cursor is already closed and deallocated...(1)'; - END CATCH; - - END; - - PRINT '-------------------------------'; - - END TRY - BEGIN CATCH - -- Handle any errors that occur during the process. - SELECT ERROR_MESSAGE() AS ErrorMessage; - - --================================================-- Log error to table --=================================================-- - IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) - VALUES ('ERROR', 'db_cursor', @DBName, ERROR_MESSAGE(),@MyProcedureName,GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --==============================================-- Change db working mode --===============================================-- - -- Ensure the database is set back to MULTI_USER mode in case of errors. - --==============================================-- Change db working mode --===============================================-- - IF (@FORCE = 1) BEGIN -- and ignore last result - PRINT 'Set the database back to MULTI_USER mode'; - SET @SQLCommand = N'ALTER DATABASE [' + @DBName + '] SET MULTI_USER; ALTER DATABASE [' + @DBName + '] SET ONLINE;'; - EXEC @return_status = sp_executesql @SQLCommand; - - IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN - SET @return_status_text = 'Setting the database to MULTI_USER mode was successfully completed!'; - PRINT @return_status_text; - END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN - SET @return_status_text = 'Setting the database to MULTI_USER mode returns warnings or has failed, check the ID!'; - PRINT @return_status_text; - END; - END; ELSE BEGIN - IF (@LOGLEVEL in ('INFO')) BEGIN - SET @return_status_text = 'Skipping to change the database mode (FORCE = OFF)!'; - PRINT @return_status_text; - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --===============================================-- Log start to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - --=====================================================-- Clean up --======================================================-- - -- Ensure temp table is droped and cursor is closed - BEGIN TRY - DROP TABLE #DBList; - END TRY - BEGIN CATCH - PRINT '#DBList Temp table are already gone...'; - END CATCH; - - IF (@REBUILDINDEX = 1) BEGIN - - BEGIN TRY - DROP TABLE #TableList; - END TRY - BEGIN CATCH - PRINT '#TableList Temp table are already gone...(2)'; - END CATCH; - - BEGIN TRY - CLOSE table_cursor; - DEALLOCATE table_cursor; - END TRY - BEGIN CATCH - PRINT 'Table cursor is already closed and deallocated...(2)'; - END CATCH; - - END; - - IF (@RECOMPILEPROCEDURES = 1) BEGIN - - BEGIN TRY - DROP TABLE #ProcedureList; - END TRY - BEGIN CATCH - PRINT '#ProcedureList Temp table are already gone...(2)'; - END CATCH; - - BEGIN TRY - CLOSE procedure_cursor; - DEALLOCATE procedure_cursor; - END TRY - BEGIN CATCH - PRINT 'Procedure cursor is already closed and deallocated...(2)'; - END CATCH; - - END; - ----------------------------------------------------------------------------------------------------------------------------- - - END CATCH; - - FETCH NEXT FROM db_cursor INTO @DBName; - - END; - - CLOSE db_cursor; - DEALLOCATE db_cursor; - - END; ELSE BEGIN - - SET @return_status = 0; - SET @return_status_text = 'So there is nothing to do...'; - PRINT @return_status_text; - - --===============================================-- Log result to table --=================================================-- - IF ((@LOGLEVEL in ('INFO','WARN','ERROR')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES ('WARN', 'db_list', 'count', @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - END; - ----------------------------------------------------------------------------------------------------------------------------- - - SET @return_status_text = 'END [PRDD_MAINTAIN_DATABASES] @ ' + CONVERT(varchar(50),GETDATE(),120); - - --================================================-- Log end to table --===================================================-- - IF ((@LOGLEVEL in ('INFO')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN - IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TBDD_MAINTAIN_DATABASES_LOG') BEGIN - INSERT INTO [TBDD_MAINTAIN_DATABASES_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) - VALUES (@LOGLEVEL, 'closing', 'procedure', @return_status, @return_status_text, @MyProcedureName, GetDate()); - END; - END; - ----------------------------------------------------------------------------------------------------------------------------- - - PRINT ''; - PRINT 'PROCEDURE - ' + @return_status_text; - PRINT '==============================='; - - Return @return_status; - -END; \ No newline at end of file diff --git a/current/[DD_SYS]-Database/[PRDD_MAINTAIN_DATABASE]/[PRDD_MAINTAIN_DATABASE].sql b/current/[DD_SYS]-Database/[PRDD_MAINTAIN_DATABASE]/[PRDD_MAINTAIN_DATABASE].sql new file mode 100644 index 0000000..7879a89 --- /dev/null +++ b/current/[DD_SYS]-Database/[PRDD_MAINTAIN_DATABASE]/[PRDD_MAINTAIN_DATABASE].sql @@ -0,0 +1,2357 @@ +USE [DD_SYS] +GO + +SET ANSI_NULLS ON +GO +SET QUOTED_IDENTIFIER ON +GO + +-- [PRDD_MAINTAIN_DATABASE] +-- ================================================================= +-- Checks database health, shrinks all database and log files +-- and recreates indizes in a maintanance run. +-- Minimum requirement: MS SQL Server 2016 +-- +-- Returns: INT Value - 0 = Everything worked well +-- ================================================================= +-- Copyright (c) 2025 by Digital Data GmbH +-- +-- Digital Data GmbH • Ludwig-Rinn-Strasse 16 • D-35452 Heuchelheim +-- Tel.: 0641/202360 • E-Mail: info-flow@digitaldata.works +-- ================================================================= +-- Creation Date / Author: 29.11.2024 / MK +-- Version Date / Editor: 23.12.2025 / MK +-- Version Number: 1.6.0.0 +-- ================================================================= +-- History: +-- 29.11.2024 / MK - First Version +-- 06.12.2024 / MK - Added "SET ONLINE" after forced SINGLE_USER Mode, Added repair parameter for DBCHECK, added @pRECOMPILEPROCEDURES, some minor error fixes +-- 13.12.2024 / MK - New way the get the procedure name, failsafe for parameters implemented +-- 20.03.2025 / MK - Improved safty checks +-- 01.12.2025 / MK - Improved text formating, Improve Index recreation, SINGLE_USER replaced with RESTRICTED_USER +-- 06.12.2025 / MK - Improved access mode while repair db +-- 08.12.2025 / MK - Extended PRINT commands and checking recovery modell of db extended +-- 13.12.2025 / MK - Improved dbshrink action, Improved logshrink action +-- 15.12.2025 / MK - Fixed issue with "user_access_desc FROM [sys].[databases]" +-- 21.12.2025 / MK - Added heap rebuild, columnstore maintenance, statistics update, file growth checks, log cleanup and error log cycling +-- 23.12.2025 / MK - Added optional system/session cache clearing and database scoped procedure cache clearing + +CREATE OR ALTER PROCEDURE [dbo].[PRDD_MAINTAIN_DATABASE] ( + @pCHECKDB BIT = 0, -- Set to 1 to make a check of the database before shrinking incl. soft repair function. Otherwise set to 0. + @pSHRINKLOG BIT = 0, -- Set to 1 to shrink the log file. Otherwise set to 0. + @pSHRINKDB BIT = 0, -- Set to 1 to shrink the database file as well. Otherwise set to 0. (Dont use on FILESTREAM databases!) + @pSHRINKTEMPDB BIT = 0, -- Set to 1 to shrink tempdb data and log files. Otherwise set to 0. + @pREBUILDINDEX BIT = 0, -- Set to 1 to rebuild or recreate all database indexes. Otherwise set to 0. + @pRECOMPILEPROCEDURES BIT = 0, -- Set to 1 to recompile all database procedures. Otherwise set to 0. + @pCLEARQUERYCACHE BIT = 0, -- Set to 1 to clear the QL query cache via DROPCLEANBUFFERS. Otherwise set to 0. + @pFREESYSTEMCACHE BIT = 0, -- Set to 1 to clear the system cache via DBCC FREESYSTEMCACHE. Otherwise set to 0. + @pFREESESSIONCACHE BIT = 0, -- Set to 1 to clear the session cache via DBCC FREESESSIONCACHE. Otherwise set to 0. + @pCLEARDBPROCCACHE BIT = 0, -- Set to 1 to clear the database scoped procedure cache. Otherwise set to 0. + @pUPDATESTATISTICS BIT = 1, -- Set to 1 to update statistics for all tables. Otherwise set to 0. + @pREBUILDHEAPS BIT = 0, -- Set to 1 to rebuild heaps with forwarded records. Otherwise set to 0. + @pMAINTAINCOLUMNSTORE BIT = 0, -- Set to 1 to maintain columnstore indexes. Otherwise set to 0. + @pCHECKFILEGROWTH BIT = 1, -- Set to 1 to check database file growth settings. Otherwise set to 0. + @pCLEANLOGS BIT = 0, -- Set to 1 to cleanup maintenance log entries. Otherwise set to 0. + @pLOGRETENTIONDAYS INT = 90, -- Set the log retention days for cleanup. Otherwise set to 0 to keep all. + @pCYCLEERRORLOGS BIT = 0, -- Set to 1 to cycle SQL Server and Agent error logs. Otherwise set to 0. + @pFORCE BIT = 0, -- Set to 1 to force the shrinking, it will cut all current sessens. In combination with @pCHECKDB it trys the soft reapair dbs. Otherwise set to 0. + @pINCLUDEDB NVARCHAR(1000) = 'DD_ECM', -- Set a list of included databases. IF <> NULL, it will override the @pEXCLUDEDB Parameter + @pEXCLUDEDB NVARCHAR(1000) = 'master,model,msdb,tempdb', -- Set a list of exluded databases. Default exclusen are the system databases. + @pLOGLEVEL NVARCHAR(25) = 'ERROR' -- Set to 'INFO','WARN' OR 'ERROR' (or NULL to disable), logging to table: [TBDD_MAINTAIN_DATABASE_LOG] +) -- 'INFO' includes Infos, Warnings and Errors, 'WARN' only warnings and Errors and 'ERROR' only Errors +AS +BEGIN + + SET NOCOUNT ON; + + -- declare new vars because of parameter sniffing + DECLARE @CHECKDB BIT = ISNULL(@pCHECKDB,0), + @SHRINKLOG BIT = ISNULL(@pSHRINKLOG,0), + @SHRINKDB BIT = ISNULL(@pSHRINKDB,0), + @SHRINKTEMPDB BIT = ISNULL(@pSHRINKTEMPDB,0), + @REBUILDINDEX BIT = ISNULL(@pREBUILDINDEX,0), + @RECOMPILEPROCEDURES BIT = ISNULL(@pRECOMPILEPROCEDURES,0), + @CLEARQUERYCACHE BIT = ISNULL(@pCLEARQUERYCACHE,0), + @FREESYSTEMCACHE BIT = ISNULL(@pFREESYSTEMCACHE,0), + @FREESESSIONCACHE BIT = ISNULL(@pFREESESSIONCACHE,0), + @CLEARDBPROCCACHE BIT = ISNULL(@pCLEARDBPROCCACHE,0), + @UPDATESTATISTICS BIT = ISNULL(@pUPDATESTATISTICS,1), + @REBUILDHEAPS BIT = ISNULL(@pREBUILDHEAPS,0), + @MAINTAINCOLUMNSTORE BIT = ISNULL(@pMAINTAINCOLUMNSTORE,0), + @CHECKFILEGROWTH BIT = ISNULL(@pCHECKFILEGROWTH,1), + @CLEANLOGS BIT = ISNULL(@pCLEANLOGS,0), + @LOGRETENTIONDAYS INT = ISNULL(@pLOGRETENTIONDAYS,90), + @CYCLEERRORLOGS BIT = ISNULL(@pCYCLEERRORLOGS,0), + @FORCE BIT = ISNULL(@pFORCE,0), + @INCLUDEDB NVARCHAR(1000) = NULLIF(LTRIM(RTRIM(@pINCLUDEDB)),''), + @EXCLUDEDB NVARCHAR(1000) = LTRIM(RTRIM(ISNULL(@pEXCLUDEDB,'master,model,msdb,tempdb'))), + @LOGLEVEL NVARCHAR(25) = UPPER(LTRIM(RTRIM(ISNULL(@pLOGLEVEL,'ERROR')))); + + -- declare runtime vars + DECLARE @MY_PROCEDURE_NAME NVARCHAR(128) = OBJECT_NAME(@@PROCID); + DECLARE @ProcedureName NVARCHAR(128) = NULL, + @DBName NVARCHAR(100) = NULL, + @DBNameCount INT = 0, + @RecoveryModelActual NVARCHAR(20) = NULL, + @RecoveryModelTarget NVARCHAR(20) = 'SIMPLE', + @UserAccessActual NVARCHAR(20) = NULL, + @UserAccessTarget NVARCHAR(20) = 'RESTRICTED_USER', + @LogFileStatus NVARCHAR(100) = NULL, + @LogFileName NVARCHAR(100) = NULL, + @CurrentLogFileSizeInMB INT = 0, + @MinimumLogFileSizeInMB INT = 0, + @MaximumLogFileSizeInMB INT = 0, + @LogIsPercentGrowth BIT = 0, + @LogGrowthValue INT = 0, + @TargetLogFileSizeInMB INT = 0, + @ProductVersion sql_variant, + @ProductMainVersion INT, + @ProductLevel sql_variant, + @ProductEdition sql_variant, + @SchemaName NVARCHAR(50), + @TableName NVARCHAR(256), + @IndexName NVARCHAR(256), + @Fragmentation FLOAT, + @IndexAction NVARCHAR(15), + @DataFileName NVARCHAR(128), + @DataFileCount INT = 0, + @TempdbFileName NVARCHAR(128), + @TempdbFileCount INT = 0, + @HeapSchemaName NVARCHAR(50), + @HeapTableName NVARCHAR(256), + @HeapListCount INT = 0, + @ColumnstoreSchemaName NVARCHAR(50), + @ColumnstoreTableName NVARCHAR(256), + @ColumnstoreIndexName NVARCHAR(256), + @ColumnstoreDeletedPercent FLOAT, + @ColumnstoreAction NVARCHAR(15), + @ColumnstoreListCount INT = 0, + @FileName NVARCHAR(128), + @FileType NVARCHAR(10), + @FileSizeInMB INT = 0, + @FileGrowthDesc NVARCHAR(50), + @FileMaxSizeInMB INT = 0, + @FileMaxSizeText NVARCHAR(20), + @FileIsPercentGrowth BIT = 0, + @FileGrowthCount INT = 0, + @LoginName NVARCHAR(50), + @HostName NVARCHAR(50), + @SessionID NVARCHAR(50) = NULL, + @MySessionID NVARCHAR(50) = @@SPID, + @SQLCommand NVARCHAR(MAX) = NULL, + @RunStart DATETIME2(0) = SYSDATETIME(), + @RunHasErrors BIT = 0, + @HasMemoryOptimizedTables BIT = 0, + @TableListCount INT = 0, + @IndexListCount INT = 0, + @ProcedureListCount INT = 0, + @RETURN_STATUS INT = 0, + @RETURN_STATUS_TEXT NVARCHAR(MAX) = 'START PROCEDURE [' + @MY_PROCEDURE_NAME + '] @ ' + CONVERT(varchar(50),GETDATE(),120), + @RETURN_ERROR_TEXT NVARCHAR(MAX) = ''; + ---------------------------------------------------------------------------------------------------------------------------- + + IF (@LOGRETENTIONDAYS < 0) BEGIN + SET @LOGRETENTIONDAYS = 0; + END; + + --=================================================-- Output parameters --================================================-- + PRINT '===================================================================================================='; + PRINT @return_status_text; + PRINT ''; + PRINT 'PARAMETER01 - @CHECKDB: ' + CONVERT(NVARCHAR(1),@CHECKDB); + PRINT 'PARAMETER02 - @SHRINKLOG: ' + CONVERT(NVARCHAR(1),@SHRINKLOG); + PRINT 'PARAMETER03 - @SHRINKDB: ' + CONVERT(NVARCHAR(1),@SHRINKDB); + PRINT 'PARAMETER04 - @SHRINKTEMPDB: ' + CONVERT(NVARCHAR(1),@SHRINKTEMPDB); + PRINT 'PARAMETER05 - @REBUILDINDEX: ' + CONVERT(NVARCHAR(1),@REBUILDINDEX); + PRINT 'PARAMETER06 - @RECOMPILEPROCEDURES: ' + CONVERT(NVARCHAR(1),@RECOMPILEPROCEDURES); + PRINT 'PARAMETER07 - @CLEARQUERYCACHE: ' + CONVERT(NVARCHAR(1),@CLEARQUERYCACHE); + PRINT 'PARAMETER08 - @FREESYSTEMCACHE: ' + CONVERT(NVARCHAR(1),@FREESYSTEMCACHE); + PRINT 'PARAMETER09 - @FREESESSIONCACHE: ' + CONVERT(NVARCHAR(1),@FREESESSIONCACHE); + PRINT 'PARAMETER10 - @CLEARDBPROCCACHE: ' + CONVERT(NVARCHAR(1),@CLEARDBPROCCACHE); + PRINT 'PARAMETER11 - @UPDATESTATISTICS: ' + CONVERT(NVARCHAR(1),@UPDATESTATISTICS); + PRINT 'PARAMETER12 - @REBUILDHEAPS: ' + CONVERT(NVARCHAR(1),@REBUILDHEAPS); + PRINT 'PARAMETER13 - @MAINTAINCOLUMNSTORE: ' + CONVERT(NVARCHAR(1),@MAINTAINCOLUMNSTORE); + PRINT 'PARAMETER14 - @CHECKFILEGROWTH: ' + CONVERT(NVARCHAR(1),@CHECKFILEGROWTH); + PRINT 'PARAMETER15 - @CLEANLOGS: ' + CONVERT(NVARCHAR(1),@CLEANLOGS); + PRINT 'PARAMETER16 - @LOGRETENTIONDAYS: ' + CONVERT(NVARCHAR(10),@LOGRETENTIONDAYS); + PRINT 'PARAMETER17 - @CYCLEERRORLOGS: ' + CONVERT(NVARCHAR(1),@CYCLEERRORLOGS); + PRINT 'PARAMETER18 - @FORCE: ' + CONVERT(NVARCHAR(1),@FORCE); + PRINT 'PARAMETER19 - @INCLUDEDB: ' + CONVERT(NVARCHAR(1000),@INCLUDEDB); + PRINT 'PARAMETER20 - @EXCLUDEDB: ' + CONVERT(NVARCHAR(1000),@EXCLUDEDB); + PRINT 'PARAMETER21 - @LOGLEVEL: ' + CONVERT(NVARCHAR(25),@LOGLEVEL); + PRINT ''; + ---------------------------------------------------------------------------------------------------------------------------- + + --=================================================-- Get server infos --==================================================-- + SELECT @ProductVersion = SERVERPROPERTY('productversion'), @ProductLevel = SERVERPROPERTY ('productlevel'), @ProductEdition = SERVERPROPERTY ('edition'); + SET @ProductMainVersion = ISNULL(LEFT(convert(NVARCHAR(100),@ProductVersion), CHARINDEX('.', convert(NVARCHAR(100),@ProductVersion)) - 1),0); + + PRINT 'Informations about this Server:'; + PRINT '@MySessionID: ' + CONVERT(NVARCHAR(100),@MySessionID); + PRINT '@ProductVersion: ' + CONVERT(NVARCHAR(100),@ProductVersion); + PRINT '@ProductMainVersion: ' + CONVERT(NVARCHAR(100),@ProductMainVersion); + PRINT '@ProductLevel: ' + CONVERT(NVARCHAR(100),@ProductLevel); + PRINT '@ProductEdition: ' + CONVERT(NVARCHAR(100),@ProductEdition); + ----------------------------------------------------------------------------------------------------------------------------- + + --==============================================-- Prepare the log table --================================================-- + IF (@LOGLEVEL is not NULL) BEGIN + PRINT ''; + IF EXISTS (SELECT * FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = N'TBDD_MAINTAIN_DATABASE_LOG') BEGIN + PRINT 'INFO: Log table already exists'; + END ELSE BEGIN + PRINT 'INFO: Log table does not exists, trying to create...'; + + CREATE TABLE [dbo].[TBDD_MAINTAIN_DATABASE_LOG]( + [GUID] [bigint] IDENTITY(1,1) NOT NULL, + [LOG_LEVEL] [nvarchar](25) NOT NULL, + [MESSAGE1] [nvarchar](max) NOT NULL, + [MESSAGE2] [nvarchar](max) NULL, + [MESSAGE3] [nvarchar](max) NULL, + [MESSAGE4] [nvarchar](max) NULL, + [MESSAGE5] [nvarchar](max) NULL, + [COMMENT] [nvarchar](max) NULL, + [ADDED_WHO] [nvarchar](50) NOT NULL, + [ADDED_WHEN] [datetime] NOT NULL, + CONSTRAINT [PK_TBDD_MAINTAIN_DATABASE_LOG_GUID] PRIMARY KEY CLUSTERED + ( + [GUID] ASC + ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION = PAGE) ON [PRIMARY] + ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]; + + ALTER TABLE [dbo].[TBDD_MAINTAIN_DATABASE_LOG] ADD CONSTRAINT [DF_TBDD_MAINTAIN_DATABASE_LOG_ADDED_WHO] DEFAULT (SUSER_SNAME()) FOR [ADDED_WHO]; + ALTER TABLE [dbo].[TBDD_MAINTAIN_DATABASE_LOG] ADD CONSTRAINT [DF_TBDD_MAINTAIN_DATABASE_LOG_ADDED_WHEN] DEFAULT (SYSDATETIME()) FOR [ADDED_WHEN]; + + END; + END; ELSE BEGIN + PRINT ''; + PRINT 'WARN: Skipping Logging to log table!'; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --===============================================-- Check for log table --================================================-- + IF NOT EXISTS (SELECT * FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = N'TBDD_MAINTAIN_DATABASE_LOG') BEGIN + SET @LOGLEVEL = 'NONE'; + PRINT 'ERROR: Because the log table [TBDD_MAINTAIN_DATABASE_LOG] does not exists, logging is disabled for this run!'; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --===============================================-- Log start to table --=================================================-- + IF ((@LOGLEVEL in ('INFO')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL,'preparing', 'procedure', @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --=============================================-- Get the dbs for the loop --==============================================-- + -- Create a temporary table to hold the table names + CREATE TABLE #DBList (DBName NVARCHAR(256)); + + IF (@ProductMainVersion >= 13) BEGIN + + IF (@INCLUDEDB is not NULL) BEGIN + INSERT INTO #DBList(DBName) + SELECT [name] as 'DBName' + FROM [master].[sys].[databases] + WHERE [name] IN (SELECT LTRIM(RTRIM([value])) FROM STRING_SPLIT(@INCLUDEDB, ',') WHERE LTRIM(RTRIM([value])) <> '') -- include these databases + AND [state] = 0 -- database is online + AND [is_in_standby] = 0; -- database is not read only for log shipping + END; ELSE BEGIN + INSERT INTO #DBList(DBName) + SELECT [name] as 'DBName' + FROM [master].[sys].[databases] + WHERE [name] NOT IN (SELECT LTRIM(RTRIM([value])) FROM STRING_SPLIT(@EXCLUDEDB, ',') WHERE LTRIM(RTRIM([value])) <> '') -- exclude these databases + AND [state] = 0 -- database is online + AND [is_in_standby] = 0; -- database is not read only for log shipping + END; + + END; ELSE BEGIN + + PRINT 'WARN: Because of the SQL Version only one DB can be processed!' + INSERT INTO #DBList(DBName) + SELECT [name] as 'DBName' + FROM [master].[sys].[databases] + WHERE [name] = ISNULL(@INCLUDEDB,DB_NAME()) -- use only this database + AND [state] = 0 -- database is online + AND [is_in_standby] = 0; -- database is not read only for log shipping + + END; + + SELECT @DBNameCount = COUNT(*) FROM #DBList; + ----------------------------------------------------------------------------------------------------------------------------- + + SET @return_status_text = 'INFO: Found ' + convert(VARCHAR,@DBNameCount) + ' database(s) to process!'; + PRINT @return_status_text; + + --===============================================-- Log result to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_list', 'count', @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --==========================================-- Create the main loop (cursor) --============================================-- + IF (@DBNameCount > 0) BEGIN + + DECLARE db_cursor CURSOR STATIC LOCAL FOR + SELECT [DBName] as 'DBName' FROM #DBList; + + OPEN db_cursor + FETCH NEXT FROM db_cursor INTO @DBName + WHILE @@FETCH_STATUS = 0 BEGIN + + PRINT ' '; + PRINT '-------------------------------'; + PRINT 'INFO: Now processing: ' + CONVERT(varchar(100),@DBName); + SET @return_status = 0; + SET @HasMemoryOptimizedTables = 0; + + --=====================================-- Detect memory-optimized tables --===========================================-- + BEGIN TRY + SET @SQLCommand = N'SELECT @HasMemoryOptimizedTablesOUT = CASE WHEN EXISTS (SELECT 1 FROM [' + @DBName + '].[sys].[tables] WHERE [is_memory_optimized] = 1) THEN 1 ELSE 0 END;'; + EXEC sp_executesql @SQLCommand, + N'@HasMemoryOptimizedTablesOUT BIT OUTPUT', + @HasMemoryOptimizedTables OUTPUT; + + IF (@HasMemoryOptimizedTables = 1) BEGIN + SET @return_status_text = 'WARN: Memory-optimized tables detected in database [' + @DBName + ']. For safety, the following actions are skipped: SHRINKDB, REBUILDHEAPS, REBUILDINDEX, MAINTAINCOLUMNSTORE.'; + PRINT @return_status_text; + END; ELSE IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: No memory-optimized tables detected in database [' + @DBName + '].'; + PRINT @return_status_text; + END; + END TRY + BEGIN CATCH + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + SET @HasMemoryOptimizedTables = 1; + SET @return_status_text = 'WARN: Detection of memory-optimized tables failed for database [' + @DBName + ']. Safety mode enabled: SHRINKDB, REBUILDHEAPS, REBUILDINDEX, MAINTAINCOLUMNSTORE are skipped.'; + PRINT @return_status_text; + END CATCH; + + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'memory_optimized', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --==============================================-- Change db working mode --===============================================-- + IF (@FORCE = 1) and (@return_status = 0) BEGIN + + PRINT 'INFO: Closing active database connections... first time'; + DECLARE kill_cursor CURSOR STATIC LOCAL FOR + SELECT CONVERT(NVARCHAR(5), [session_id]) as 'SessionID', + CONVERT(NVARCHAR(50), [login_name]) as 'LoginName', + CONVERT(NVARCHAR(50), [host_name]) as 'HostName' + FROM [master].[sys].[dm_exec_sessions] + WHERE [is_user_process] = 1 + AND [database_id] = db_id(@DBName) + AND [session_id] <> @MySessionID; + + OPEN kill_cursor; + FETCH NEXT FROM kill_cursor INTO @SessionID, @LoginName, @HostName; + WHILE @@FETCH_STATUS = 0 BEGIN + + BEGIN TRY + SET @SQLCommand = 'KILL ' + @SessionID + ';'; + PRINT 'INFO: Killing @SessionID: ' + @SessionID + ', @LoginName: ' + @LoginName + ', @HostName: ' + @HostName; + EXEC sp_executesql @SQLCommand; + SET @SQLCommand = NULL; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Closing connections to database was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Closing connections to database returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'kill_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + SET @SQLCommand = NULL; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN','ERROR')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'kill_cursor', @DBName, ERROR_MESSAGE(),@MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END CATCH; + + FETCH NEXT FROM kill_cursor INTO @SessionID, @LoginName, @HostName; + + END; + + CLOSE kill_cursor; + DEALLOCATE kill_cursor; + ----------------------------------------------------------------------------------------------------------------------------- + + + --=========================================-- Get current user access level --=============================================-- + SET @UserAccessActual = (SELECT [user_access_desc] FROM [sys].[databases] WHERE NAME = @DBName); + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Currently the user access level is set to: ' + @UserAccessActual; + PRINT @return_status_text; + END ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Currently the user access level is set to: ' + @UserAccessActual; + PRINT @return_status_text; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL,'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --==============================================-- Switch db operation mode --=============================================-- + IF (@CHECKDB = 1) BEGIN + + PRINT 'INFO: Set the database to SINGLE_USER mode to avoid multiple active sessions.'; + SET @SQLCommand = N'ALTER DATABASE [' + @DBName + '] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Setting the database to SINGLE_USER mode was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Setting the database to SINGLE_USER mode returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + END; ELSE BEGIN + + PRINT 'INFO: Set the database to ' + @UserAccessTarget + ' mode to avoid multiple active sessions.'; + SET @SQLCommand = N'ALTER DATABASE [' + @DBName + '] SET ' + @UserAccessTarget + ' WITH ROLLBACK IMMEDIATE;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Setting the database to ' + @UserAccessTarget + ' mode was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Setting the database to ' + @UserAccessTarget + ' mode returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END; ELSE BEGIN + + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping to change the database mode (FORCE = OFF)!'; + PRINT @return_status_text; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Check db consistency --================================================-- + IF (@CHECKDB = 1) and (@return_status = 0) BEGIN + + --==========================================-- Get current recovery modell --==============================================-- + SET @RecoveryModelActual = (SELECT [recovery_model_desc] FROM [sys].[databases] WHERE NAME = @DBName); + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Currently the recovery mode is set to: ' + @RecoveryModelActual; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Currently the recovery mode is set to: ' + @RecoveryModelActual; + PRINT @return_status_text; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL,'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + IF (@FORCE = 1) BEGIN + PRINT 'INFO: Check the database for consistency and health and try soft repair.'; + SET @SQLCommand = N'USE [' + @DBName + ']; + ALTER DATABASE [' + @DBName + '] SET RECOVERY ' + @RecoveryModelTarget + '; + DBCC CHECKDB ([' + @DBName + '],REPAIR_REBUILD) WITH NO_INFOMSGS, ALL_ERRORMSGS; + ALTER DATABASE [' + @DBName + '] SET RECOVERY ' + @RecoveryModelActual + ';'; + END; ELSE BEGIN + PRINT 'INFO: Check the database for consistency and health.'; + SET @SQLCommand = N'USE [' + @DBName + ']; + ALTER DATABASE [' + @DBName + '] SET RECOVERY ' + @RecoveryModelTarget + '; + DBCC CHECKDB ([' + @DBName + ']) WITH NO_INFOMSGS, ALL_ERRORMSGS; + ALTER DATABASE [' + @DBName + '] SET RECOVERY ' + @RecoveryModelActual + ';'; + END; + + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Check the database for consistency and health was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Check the database for consistency and health returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + END; ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping the database check for consistency and health!'; + PRINT @return_status_text; + END; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --=========================================-- Get current user access level --=============================================-- + SET @UserAccessActual = (SELECT [user_access_desc] FROM [sys].[databases] WHERE NAME = @DBName); + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Currently the user access level is set to: ' + @UserAccessActual; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Currently the user access level is set to: ' + @UserAccessActual; + PRINT @return_status_text; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL,'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --===========================================-- Switch db operation mode back --===========================================-- + IF ((@CHECKDB <> 1) AND (@UserAccessActual = @UserAccessTarget)) BEGIN + + PRINT 'INFO: Set the database back to MULTI_USER mode to allow multiple active sessions.'; + SET @SQLCommand = N'ALTER DATABASE [' + @DBName + '] SET MULTI_USER WITH ROLLBACK IMMEDIATE;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Setting the database to MULTI_USER mode was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Setting the database to MULTI_USER mode returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --==========================================-- Rebuild heaps with forwarded records --========================================-- + IF ((@REBUILDHEAPS = 1) and (@return_status = 0) and (@HasMemoryOptimizedTables = 0)) BEGIN + PRINT 'INFO: Prepare to rebuild heaps with forwarded records.'; + + BEGIN TRY + DROP TABLE #HeapList; + END TRY BEGIN CATCH + PRINT 'INFO: #HeapList Temp table is already gone... (1)'; + END CATCH; + + -- Create a temporary table to hold heap tables + CREATE TABLE #HeapList (SchemaName NVARCHAR(50), TableName NVARCHAR(256)); + + -- Set the context to the specified database and fill the temporary table + SET @SQLCommand = 'INSERT INTO #HeapList (SchemaName, TableName) + SELECT DISTINCT s.[name], t.[name] + FROM [' + @DBName + '].[sys].[tables] t + INNER JOIN [' + @DBName + '].[sys].[schemas] s ON t.[schema_id] = s.[schema_id] + INNER JOIN [' + @DBName + '].[sys].[indexes] i ON t.[object_id] = i.[object_id] + INNER JOIN sys.dm_db_index_physical_stats(DB_ID(''' + @DBName + '''), NULL, NULL, NULL, ''LIMITED'') ips + ON i.[object_id] = ips.[object_id] AND i.[index_id] = ips.[index_id] + WHERE i.[type] = 0 AND ips.[index_id] = 0 AND ips.forwarded_record_count > 0;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Getting the heaps was successfully done!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Getting the heaps returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + IF (@return_status = 0) BEGIN + SELECT @HeapListCount = COUNT(*) FROM #HeapList; + PRINT 'INFO: Found: ' + convert(VARCHAR,@HeapListCount) + ' heaps to rebuild'; + END; ELSE BEGIN + SET @HeapListCount = 0; + PRINT 'WARN: Found: no heaps to rebuild'; + END; + + END; ELSE BEGIN + IF ((@REBUILDHEAPS = 1) and (@HasMemoryOptimizedTables = 1)) BEGIN + SET @return_status_text = 'WARN: Skipping rebuilding heaps because memory-optimized tables were detected in database [' + @DBName + '].'; + PRINT @return_status_text; + END; ELSE IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping rebuilding heaps!'; + PRINT @return_status_text; + END; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + IF ((@REBUILDHEAPS = 1) and (@return_status = 0) and (@HeapListCount > 0) and (@HasMemoryOptimizedTables = 0)) BEGIN + + DECLARE heap_cursor CURSOR STATIC LOCAL FOR + SELECT SchemaName, TableName FROM #HeapList; + + OPEN heap_cursor; + FETCH NEXT FROM heap_cursor INTO @HeapSchemaName, @HeapTableName; + WHILE @@FETCH_STATUS = 0 BEGIN + BEGIN TRY + + PRINT 'INFO: Rebuild heap table: ' + CONVERT(VARCHAR(1000),@HeapSchemaName + '.' + @HeapTableName); + SET @SQLCommand = 'USE [' + @DBName + ']; ALTER TABLE [' + @HeapSchemaName + '].[' + @HeapTableName + '] REBUILD;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Heap table [' + @HeapSchemaName + '].[' + @HeapTableName + '] successfully rebuilt!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Heap table [' + @HeapSchemaName + '].[' + @HeapTableName + '] returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'heap_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + --================================================-- Log error to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'heap_cursor', @DBName, ERROR_MESSAGE(),@MY_PROCEDURE_NAME, GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + END CATCH; + + FETCH NEXT FROM heap_cursor INTO @HeapSchemaName, @HeapTableName; + + END; + + CLOSE heap_cursor; + DEALLOCATE heap_cursor; + + -- Drop the temporary table + DROP TABLE #HeapList; + + SET @return_status_text = 'INFO: Rebuilding heaps successfully completed!'; + PRINT @return_status_text; + + END; ELSE BEGIN + IF ((@REBUILDHEAPS = 1) and (@HasMemoryOptimizedTables = 1)) BEGIN + SET @return_status_text = 'WARN: Skipping heap rebuild execution because memory-optimized tables were detected in database [' + @DBName + '].'; + PRINT @return_status_text; + END; ELSE IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping the heap rebuild execution!'; + PRINT @return_status_text; + END; + END; + ------------------------------------------------------------------------------------------------------------------------------ + + --=============================================-- Rebuild database indexes --==============================================-- + IF ((@REBUILDINDEX = 1) and (@return_status = 0) and (@HasMemoryOptimizedTables = 0)) BEGIN + PRINT 'INFO: Prepare to maintain database indexes (reorganize/rebuild by fragmentation).'; + + BEGIN TRY + DROP TABLE #TableList; + END TRY BEGIN CATCH + PRINT 'INFO: #TableList Temp table is already gone... (1)'; + END CATCH; + + BEGIN TRY + DROP TABLE #IndexList; + END TRY BEGIN CATCH + PRINT 'INFO: #IndexList Temp table is already gone... (1)'; + END CATCH; + + -- Create a temporary table to hold the table names + CREATE TABLE #TableList (SchemaName NVARCHAR(50), TableName NVARCHAR(256)); + + -- Set the context to the specified database and fill the temporary table + SET @SQLCommand = ' INSERT INTO #TableList (SchemaName, TableName) + SELECT DISTINCT (SCHEMA_NAME([schema_id])), t.[name] + FROM [' + @DBName + '].[sys].[tables] t + INNER JOIN [' + @DBName + '].[sys].[indexes] i ON t.[object_id] = i.[object_id] + WHERE i.[type] IN (1, 2) -- Clustered and Non-Clustered indexes;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Getting the tables was successfully done!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Getting the tables returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + IF (@return_status = 0) BEGIN + SELECT @TableListCount = COUNT(*) FROM #TableList; + PRINT 'INFO: Found: ' + convert(VARCHAR,@TableListCount) + ' tables to reindex'; + END; ELSE BEGIN + SET @TableListCount = 0; + PRINT 'WARN: Found: no tables to reindex'; + END; + + END; ELSE BEGIN + IF ((@REBUILDINDEX = 1) and (@HasMemoryOptimizedTables = 1)) BEGIN + SET @return_status_text = 'WARN: Skipping rebuilding database indexes because memory-optimized tables were detected in database [' + @DBName + '].'; + PRINT @return_status_text; + END; ELSE IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping rebuilding database indexes!'; + PRINT @return_status_text; + END; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + IF ((@REBUILDINDEX = 1) and (@return_status = 0) and (@TableListCount > 0) and (@HasMemoryOptimizedTables = 0)) BEGIN + + DECLARE table_cursor CURSOR STATIC LOCAL FOR + SELECT SchemaName, TableName FROM #TableList; + + OPEN table_cursor; + FETCH NEXT FROM table_cursor INTO @SchemaName, @TableName; + WHILE @@FETCH_STATUS = 0 BEGIN + BEGIN TRY + + PRINT 'INFO: Maintain indexes for table: ' + CONVERT(VARCHAR(1000),@TableName); + SET @IndexListCount = 0; + + --======================================-- Prepare index list with fragmentation --=======================================-- + BEGIN TRY + DROP TABLE #IndexList; + END TRY BEGIN CATCH + PRINT 'INFO: #IndexList Temp table is already gone... (2)'; + END CATCH; + + CREATE TABLE #IndexList (IndexName NVARCHAR(256), Fragmentation FLOAT); + + SET @SQLCommand = 'INSERT INTO #IndexList (IndexName, Fragmentation) + SELECT i.[name], ips.avg_fragmentation_in_percent + FROM sys.dm_db_index_physical_stats(DB_ID(''' + @DBName + '''), OBJECT_ID(''' + QUOTENAME(@DBName) + '.' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@TableName) + '''), NULL, NULL, ''LIMITED'') ips + INNER JOIN [' + @DBName + '].sys.indexes i ON ips.[object_id] = i.[object_id] AND ips.[index_id] = i.[index_id] + WHERE i.[type] IN (1,2) AND ips.[index_id] > 0 AND ips.avg_fragmentation_in_percent IS NOT NULL;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Collected index fragmentation data for table [' + @TableName + ']'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Collecting index fragmentation data for table [' + @TableName + '] returned warnings or failed, check the ID!'; + PRINT @return_status_text; + END; + + SELECT @IndexListCount = COUNT(*) FROM #IndexList; + PRINT 'INFO: Found: ' + CONVERT(VARCHAR,@IndexListCount) + ' indexes to process'; + + --======================================-- Process indexes per fragmentation --=======================================-- + IF (@IndexListCount > 0) BEGIN + + DECLARE index_cursor CURSOR STATIC LOCAL FOR + SELECT IndexName, Fragmentation FROM #IndexList; + + OPEN index_cursor; + FETCH NEXT FROM index_cursor INTO @IndexName, @Fragmentation; + WHILE @@FETCH_STATUS = 0 BEGIN + BEGIN TRY + + SET @IndexAction = CASE + + WHEN @Fragmentation < 5 + THEN 'NOTHING' + + WHEN @Fragmentation >= 5 AND @Fragmentation < 30 + THEN 'REORGANIZE' + + ELSE 'REBUILD' + + END; + + PRINT 'INFO: Maintain index: ' + CONVERT(VARCHAR(1000),@IndexName) + ' (Fragmentation: ' + CONVERT(VARCHAR(20),@Fragmentation) + '%) -> Do: ' + @IndexAction; + + IF (@IndexAction = 'REORGANIZE') BEGIN + SET @SQLCommand = 'ALTER INDEX [' + @IndexName + '] ON [' + @DBName + '].[' + @SchemaName + '].[' + @TableName + '] REORGANIZE;'; + END; ELSE IF (@IndexAction = 'REBUILD') BEGIN + SET @SQLCommand = 'ALTER INDEX [' + @IndexName + '] ON [' + @DBName + '].[' + @SchemaName + '].[' + @TableName + '] REBUILD WITH (FILLFACTOR = 80, SORT_IN_TEMPDB = ON, STATISTICS_NORECOMPUTE = ON);'; + END; ELSE BEGIN + SET @SQLCommand = NULL; + PRINT 'INFO: Skipping index maintenance for index [' + @IndexName + '] because fragmentation is below threshold.'; + END; + + IF (@SQLCommand is not NULL) BEGIN + EXEC @return_status = sp_executesql @SQLCommand; + END; ELSE BEGIN + SET @return_status = 0; + END; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Index [' + @IndexName + ']; "Do: ' + LOWER(@IndexAction) + '" successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Index [' + @IndexName + ']; "Do: ' + LOWER(@IndexAction) + '" returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'index_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + --================================================-- Log error to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'index_cursor', @DBName, ERROR_MESSAGE(),@MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END CATCH; + + FETCH NEXT FROM index_cursor INTO @IndexName, @Fragmentation; + END; + + CLOSE index_cursor; + DEALLOCATE index_cursor; + + END; ELSE BEGIN + PRINT 'WARN: No indexes found to reorganize or rebuild on table [' + @TableName + ']'; + END; + + -- Drop the temporary table + DROP TABLE #IndexList; + + SET @return_status_text = 'INFO: Index maintenance for table [' + @TableName + '] successfully completed!'; + PRINT @return_status_text; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'table_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + -- Short break to avoid access violations + WAITFOR DELAY '00:00:03'; + + ----------------------------------------------------------------------------------------------------------------------------- + IF (@FORCE = 1) BEGIN + PRINT 'INFO: Closing active database connections... again'; + DECLARE kill_cursor CURSOR STATIC LOCAL FOR + SELECT CONVERT(NVARCHAR(5), [session_id]) as 'SessionID', + CONVERT(NVARCHAR(50), [login_name]) as 'LoginName', + CONVERT(NVARCHAR(50), [host_name]) as 'HostName' + FROM [master].[sys].[dm_exec_sessions] + WHERE [is_user_process] = 1 + AND [database_id] = db_id(@DBName) + AND [session_id] <> @MySessionID; + + OPEN kill_cursor; + FETCH NEXT FROM kill_cursor INTO @SessionID, @LoginName, @HostName; + WHILE @@FETCH_STATUS = 0 BEGIN + + BEGIN TRY + + SET @SQLCommand = 'KILL ' + @SessionID + ';'; + PRINT 'INFO: Killing @SessionID: ' + @SessionID + ', @LoginName: ' + @LoginName + ', @HostName: ' + @HostName; + EXEC sp_executesql @SQLCommand; + SET @SQLCommand = NULL; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Closing connections to database was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Closing connections to database returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'kill_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + SET @SQLCommand = NULL; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN','ERROR')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'kill_cursor', @DBName, ERROR_MESSAGE(),@MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END CATCH; + + FETCH NEXT FROM kill_cursor INTO @SessionID, @LoginName, @HostName; + + END; + + CLOSE kill_cursor; + DEALLOCATE kill_cursor; + ----------------------------------------------------------------------------------------------------------------------------- + END; + + FETCH NEXT FROM table_cursor INTO @SchemaName, @TableName; + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + --================================================-- Log error to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'table_cursor', @DBName, ERROR_MESSAGE(),@MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + FETCH NEXT FROM table_cursor INTO @SchemaName, @TableName; + + END CATCH; + + END; + + CLOSE table_cursor; + DEALLOCATE table_cursor; + + -- Drop the temporary table + DROP TABLE #TableList; + + SET @return_status_text = 'INFO: Recreation of the database indexes successfully completed!'; + PRINT @return_status_text; + + END; ELSE BEGIN + IF ((@REBUILDINDEX = 1) and (@HasMemoryOptimizedTables = 1)) BEGIN + SET @return_status_text = 'WARN: Skipping index maintenance execution because memory-optimized tables were detected in database [' + @DBName + '].'; + PRINT @return_status_text; + END; ELSE IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping the recreation of the database indexes!'; + PRINT @return_status_text; + END; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --========================================-- Maintain columnstore indexes --===========================================-- + IF ((@MAINTAINCOLUMNSTORE = 1) and (@return_status = 0) and (@HasMemoryOptimizedTables = 0)) BEGIN + PRINT 'INFO: Prepare to maintain columnstore indexes.'; + + BEGIN TRY + DROP TABLE #ColumnstoreList; + END TRY BEGIN CATCH + PRINT 'INFO: #ColumnstoreList Temp table is already gone... (1)'; + END CATCH; + + CREATE TABLE #ColumnstoreList (SchemaName NVARCHAR(50), TableName NVARCHAR(256), IndexName NVARCHAR(256), DeletedPercent FLOAT); + + SET @SQLCommand = 'USE [' + @DBName + ']; + INSERT INTO #ColumnstoreList (SchemaName, TableName, IndexName, DeletedPercent) + SELECT s.[name], t.[name], i.[name], + CASE WHEN SUM(rg.total_rows) = 0 THEN 0 + ELSE (SUM(rg.deleted_rows) * 100.0 / SUM(rg.total_rows)) + END + FROM sys.[tables] t + INNER JOIN sys.[schemas] s ON t.[schema_id] = s.[schema_id] + INNER JOIN sys.[indexes] i ON t.[object_id] = i.[object_id] + INNER JOIN sys.dm_db_column_store_row_group_physical_stats rg ON i.[object_id] = rg.[object_id] AND i.[index_id] = rg.[index_id] + WHERE i.[type] IN (5,6) + GROUP BY s.[name], t.[name], i.[name];'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Getting the columnstore indexes was successfully done!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Getting the columnstore indexes returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + IF (@return_status = 0) BEGIN + SELECT @ColumnstoreListCount = COUNT(*) FROM #ColumnstoreList; + PRINT 'INFO: Found: ' + convert(VARCHAR,@ColumnstoreListCount) + ' columnstore index(es) to maintain'; + END; ELSE BEGIN + SET @ColumnstoreListCount = 0; + PRINT 'WARN: Found: no columnstore indexes to maintain'; + END; + + END; ELSE BEGIN + IF ((@MAINTAINCOLUMNSTORE = 1) and (@HasMemoryOptimizedTables = 1)) BEGIN + SET @return_status_text = 'WARN: Skipping columnstore maintenance because memory-optimized tables were detected in database [' + @DBName + '].'; + PRINT @return_status_text; + END; ELSE IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping columnstore maintenance!'; + PRINT @return_status_text; + END; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + IF ((@MAINTAINCOLUMNSTORE = 1) and (@return_status = 0) and (@ColumnstoreListCount > 0) and (@HasMemoryOptimizedTables = 0)) BEGIN + + DECLARE columnstore_cursor CURSOR STATIC LOCAL FOR + SELECT SchemaName, TableName, IndexName, DeletedPercent FROM #ColumnstoreList; + + OPEN columnstore_cursor; + FETCH NEXT FROM columnstore_cursor INTO @ColumnstoreSchemaName, @ColumnstoreTableName, @ColumnstoreIndexName, @ColumnstoreDeletedPercent; + WHILE @@FETCH_STATUS = 0 BEGIN + BEGIN TRY + + SET @ColumnstoreAction = CASE + WHEN @ColumnstoreDeletedPercent >= 20 THEN 'REBUILD' + WHEN @ColumnstoreDeletedPercent >= 10 THEN 'REORGANIZE' + ELSE 'NOTHING' + END; + + PRINT 'INFO: Maintain columnstore index: ' + CONVERT(VARCHAR(1000),@ColumnstoreIndexName) + ' (Deleted: ' + CONVERT(VARCHAR(20),@ColumnstoreDeletedPercent) + '%) -> Do: ' + @ColumnstoreAction; + + IF (@ColumnstoreAction = 'REORGANIZE') BEGIN + SET @SQLCommand = 'USE [' + @DBName + ']; ALTER INDEX [' + @ColumnstoreIndexName + '] ON [' + @ColumnstoreSchemaName + '].[' + @ColumnstoreTableName + '] REORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON);'; + END; ELSE IF (@ColumnstoreAction = 'REBUILD') BEGIN + SET @SQLCommand = 'USE [' + @DBName + ']; ALTER INDEX [' + @ColumnstoreIndexName + '] ON [' + @ColumnstoreSchemaName + '].[' + @ColumnstoreTableName + '] REBUILD;'; + END; ELSE BEGIN + SET @SQLCommand = NULL; + PRINT 'INFO: Skipping columnstore maintenance for index [' + @ColumnstoreIndexName + '] because deleted rows are below threshold.'; + END; + + IF (@SQLCommand is not NULL) BEGIN + EXEC @return_status = sp_executesql @SQLCommand; + END; ELSE BEGIN + SET @return_status = 0; + END; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Columnstore index [' + @ColumnstoreIndexName + ']; "Do: ' + LOWER(@ColumnstoreAction) + '" successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Columnstore index [' + @ColumnstoreIndexName + ']; "Do: ' + LOWER(@ColumnstoreAction) + '" returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'columnstore_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + --================================================-- Log error to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'columnstore_cursor', @DBName, ERROR_MESSAGE(),@MY_PROCEDURE_NAME, GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + END CATCH; + + FETCH NEXT FROM columnstore_cursor INTO @ColumnstoreSchemaName, @ColumnstoreTableName, @ColumnstoreIndexName, @ColumnstoreDeletedPercent; + + END; + + CLOSE columnstore_cursor; + DEALLOCATE columnstore_cursor; + + -- Drop the temporary table + DROP TABLE #ColumnstoreList; + + SET @return_status_text = 'INFO: Columnstore maintenance successfully completed!'; + PRINT @return_status_text; + + END; ELSE BEGIN + IF ((@MAINTAINCOLUMNSTORE = 1) and (@HasMemoryOptimizedTables = 1)) BEGIN + SET @return_status_text = 'WARN: Skipping columnstore maintenance execution because memory-optimized tables were detected in database [' + @DBName + '].'; + PRINT @return_status_text; + END; ELSE IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping the columnstore maintenance execution!'; + PRINT @return_status_text; + END; + END; + ------------------------------------------------------------------------------------------------------------------------------ + + --==============================================-- Update statistics --==============================================-- + IF ((@UPDATESTATISTICS = 1) and (@return_status = 0)) BEGIN + PRINT 'INFO: Update statistics for database tables.'; + SET @SQLCommand = N'USE [' + @DBName + ']; EXEC sp_updatestats;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Updating statistics was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Updating statistics returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + END; ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping updating statistics!'; + PRINT @return_status_text; + END; + END; + ------------------------------------------------------------------------------------------------------------------------------ + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + --=========================================-- Prepare to recompile Procedures --===========================================-- + IF ((@RECOMPILEPROCEDURES = 1) and (@return_status = 0)) BEGIN + PRINT 'INFO: Prepare to recompile database procedures.'; + + BEGIN TRY + DROP TABLE #ProcedureList; + END TRY BEGIN CATCH + PRINT 'INFO: #ProcedureList Temp table is already gone... (1)'; + END CATCH; + + -- Create a temporary table to hold the procedure names + CREATE TABLE #ProcedureList (SchemaName NVARCHAR(50), ProcedureName NVARCHAR(256)); + + -- Set the context to the specified database and fill the temporary table + SET @SQLCommand = ' INSERT INTO #ProcedureList (SchemaName, ProcedureName) + SELECT SPECIFIC_SCHEMA, SPECIFIC_NAME + FROM [' + @DBName + '].INFORMATION_SCHEMA.ROUTINES + WHERE ROUTINE_TYPE = ''PROCEDURE'' + AND SPECIFIC_NAME <> ''' + @MY_PROCEDURE_NAME + ''' + ORDER BY SPECIFIC_NAME;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Getting the procedures was successfully done!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Getting the procedures returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + IF (@return_status = 0) BEGIN + SELECT @ProcedureListCount = COUNT(*) FROM #ProcedureList; + PRINT 'INFO: Found: ' + convert(VARCHAR,@ProcedureListCount) + ' procedures to recompile'; + END; ELSE BEGIN + SET @ProcedureListCount = 0; + PRINT 'WARN: Found: no procedures to recompile'; + END; + + END; ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping recompiling procedures!'; + PRINT @return_status_text; + END; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Recompile Procedures --================================================-- + IF ((@RECOMPILEPROCEDURES = 1) and (@return_status = 0) and (@ProcedureListCount > 0)) BEGIN + + PRINT 'INFO: Removing all procedure elements from the query plan cache.'; + SET @SQLCommand = N'USE [' + @DBName + ']; DBCC FREEPROCCACHE;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Removing all procedure elements was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Removing all procedure elements returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'procedure_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + DECLARE procedure_cursor CURSOR STATIC LOCAL FOR + SELECT SchemaName, ProcedureName FROM #ProcedureList; + + OPEN procedure_cursor; + FETCH NEXT FROM procedure_cursor INTO @SchemaName, @ProcedureName; + WHILE @@FETCH_STATUS = 0 BEGIN + BEGIN TRY + + PRINT 'INFO: Recompile procedure: ' + CONVERT(VARCHAR(1000),@SchemaName + '.' + @ProcedureName); + SET @SQLCommand = 'USE [' + @DBName + ']; EXEC sp_recompile ''' + @SchemaName + '.' + @ProcedureName + '''; '; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Recompiling the procedure ' + @SchemaName + '.' + @ProcedureName + ' successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Recompiling the procedure ' + @SchemaName + '.' + @ProcedureName + ' returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'procedure_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + FETCH NEXT FROM procedure_cursor INTO @SchemaName, @ProcedureName; + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + --================================================-- Log error to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'procedure_cursor', @DBName, ERROR_MESSAGE(),@MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + FETCH NEXT FROM procedure_cursor INTO @SchemaName, @ProcedureName; + + END CATCH; + + END; + + CLOSE procedure_cursor; + DEALLOCATE procedure_cursor; + + -- Drop the temporary table + DROP TABLE #ProcedureList; + + SET @return_status_text = 'INFO: Recompiling of procedures successfully completed!'; + PRINT @return_status_text; + + END; ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping the recompiling of the procedures!'; + PRINT @return_status_text; + END; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'procedure_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --=========================================-- Clear database scoped procedure cache --===========================================-- + IF ((@CLEARDBPROCCACHE = 1) and (@return_status = 0)) BEGIN + + IF (@ProductMainVersion >= 13) BEGIN + + PRINT 'INFO: Clearing database scoped procedure cache.'; + SET @SQLCommand = N'USE [' + @DBName + ']; ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE CACHE;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Clearing database scoped procedure cache was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Clearing database scoped procedure cache returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + END; ELSE BEGIN + IF (@LOGLEVEL in ('WARN')) BEGIN + SET @return_status_text = 'WARN: Skipping database scoped procedure cache clearing, requires SQL Server 2016 or newer!'; + PRINT @return_status_text; + END; + END; + + END; ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping database scoped procedure cache clearing!'; + PRINT @return_status_text; + END; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --================================================-- Clear Query Cache --==================================================-- + + IF ((@CLEARQUERYCACHE = 1) and (@return_status = 0)) BEGIN + + PRINT 'INFO: Removing the sql query cache.'; + SET @SQLCommand = N'USE [' + @DBName + ']; DBCC DROPCLEANBUFFERS;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Removing sql query cache was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Removing sql query cache returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + END; ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping the query cache clearing!'; + PRINT @return_status_text; + END; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --================================================-- Shrink db as well --==================================================-- + IF ((@SHRINKDB = 1) and (@return_status = 0) and (@HasMemoryOptimizedTables = 0)) BEGIN + PRINT 'INFO: Shrink data files to reclaim unused space.'; -- do this after indexes are rebuilt + + BEGIN TRY + DROP TABLE #DataFileList; + END TRY BEGIN CATCH + PRINT 'INFO: #DataFileList Temp table is already gone...'; + END CATCH; + + CREATE TABLE #DataFileList (FileName NVARCHAR(128)); + + INSERT INTO #DataFileList(FileName) + SELECT [name] + FROM [master].[sys].[master_files] + WHERE [database_id] = DB_ID(@DBName) + AND [type_desc] = 'ROWS'; + + SELECT @DataFileCount = COUNT(*) FROM #DataFileList; + PRINT 'INFO: Found: ' + CONVERT(VARCHAR,@DataFileCount) + ' data file(s) to shrink.'; + + IF (@DataFileCount > 0) BEGIN + + DECLARE datafile_cursor CURSOR STATIC LOCAL FOR + SELECT FileName FROM #DataFileList; + + OPEN datafile_cursor; + FETCH NEXT FROM datafile_cursor INTO @DataFileName; + WHILE @@FETCH_STATUS = 0 BEGIN + BEGIN TRY + + -- Shrink each data file to minimum size (target 0 lets SQL decide). + SET @SQLCommand = N'USE [' + @DBName + ']; DBCC SHRINKFILE (N''' + @DataFileName + ''', 0) WITH NO_INFOMSGS;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Shrinking data file [' + @DataFileName + '] was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Shrinking data file [' + @DataFileName + '] returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'datafile_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + --================================================-- Log error to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'datafile_cursor', @DBName, ERROR_MESSAGE(),@MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END CATCH; + + FETCH NEXT FROM datafile_cursor INTO @DataFileName; + END; + + CLOSE datafile_cursor; + DEALLOCATE datafile_cursor; + + END; ELSE BEGIN + PRINT 'INFO: No data files found to shrink.'; + END; + + BEGIN TRY + DROP TABLE #DataFileList; + END TRY BEGIN CATCH + PRINT 'INFO: #DataFileList Temp table is already gone...'; + END CATCH; + + END; ELSE BEGIN + IF ((@SHRINKDB = 1) and (@HasMemoryOptimizedTables = 1)) BEGIN + SET @return_status_text = 'WARN: Skipping shrinking database files because memory-optimized tables were detected in database [' + @DBName + '].'; + PRINT @return_status_text; + END; ELSE IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping shrinking the database!'; + PRINT @return_status_text; + END; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --======================================-- Check if shrinking the log is possible --=======================================-- + IF (@SHRINKLOG = 1) BEGIN + + --==========================================-- Get current recovery modell --==============================================-- + SET @RecoveryModelActual = (SELECT [recovery_model_desc] FROM [sys].[databases] WHERE NAME = @DBName); + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Currently the recovery mode is set to: ' + @RecoveryModelActual; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Currently the recovery mode is set to: ' + @RecoveryModelActual; + PRINT @return_status_text; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL,'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===========================================-- Get infos about the log file --============================================-- + PRINT 'INFO: Get the current and minimum size of the log file in MB.'; + SET @SQLCommand = N'SELECT TOP 1 @LogFileName = [name], @CurrentLogFileSizeInMB = ([size] / 128), @LogGrowthValue = [growth], @LogIsPercentGrowth = [is_percent_growth], @MinimumLogFileSizeInMB = CASE WHEN [is_percent_growth] = 1 THEN 0 ELSE ([growth] / 128) END, @MaximumLogFileSizeInMB = CASE WHEN [max_size] = -1 THEN 0 ELSE ([max_size] / 128) END + FROM [master].[sys].[master_files] + WHERE [database_id] = DB_ID(''' + @DBName + ''') AND [STATE_DESC] = ''ONLINE'' AND [type_desc] = ''LOG'''; + EXEC @return_status = sp_executesql @SQLCommand, N'@LogFileName VARCHAR(100) OUTPUT, @CurrentLogFileSizeInMB INT OUTPUT, @LogGrowthValue INT OUTPUT, @LogIsPercentGrowth BIT OUTPUT, @MinimumLogFileSizeInMB INT OUTPUT, @MaximumLogFileSizeInMB INT OUTPUT', + @LogFileName OUTPUT, @CurrentLogFileSizeInMB OUTPUT, @LogGrowthValue OUTPUT, @LogIsPercentGrowth OUTPUT, @MinimumLogFileSizeInMB OUTPUT, @MaximumLogFileSizeInMB OUTPUT; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Getting log file settings was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Getting log file settings returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===========================================-- Calculate target file size --==============================================-- + PRINT 'INFO: The @CurrentLogFileSizeInMB is: ' + CONVERT(VARCHAR(1000),@CurrentLogFileSizeInMB); + PRINT 'INFO: The @LogGrowthValue is: ' + CONVERT(VARCHAR(1000),@LogGrowthValue); + PRINT 'INFO: The @LogIsPercentGrowth is: ' + CONVERT(VARCHAR(10),@LogIsPercentGrowth); + PRINT 'INFO: The @MinimumLogFileSizeInMB is: ' + CONVERT(VARCHAR(1000),@MinimumLogFileSizeInMB); + PRINT 'INFO: The @MaximumLogFileSizeInMB is: ' + CONVERT(VARCHAR(1000),@MaximumLogFileSizeInMB); + + SET @TargetLogFileSizeInMB = CASE + WHEN (@CurrentLogFileSizeInMB <= 0) THEN 0 + WHEN (@MinimumLogFileSizeInMB > 0) AND (CEILING(@CurrentLogFileSizeInMB * 0.1) < @MinimumLogFileSizeInMB) THEN @MinimumLogFileSizeInMB + ELSE CEILING(@CurrentLogFileSizeInMB * 0.1) + END; + + PRINT 'INFO: The @TargetLogFileSizeInMB is: ' + CONVERT(VARCHAR(1000),@TargetLogFileSizeInMB); + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --=======================================-- Needs the recovery modell to be reset? --======================================-- + IF ((@CurrentLogFileSizeInMB > @TargetLogFileSizeInMB) AND (@RecoveryModelActual <> @RecoveryModelTarget)) BEGIN + + --=============================================-- Change db recovery mode --===============================================-- + PRINT 'INFO: Truncating the log needs to change the database recovery model to: ' + @RecoveryModelTarget + '.'; + SET @SQLCommand = N'ALTER DATABASE [' + @DBName + '] SET RECOVERY ' + @RecoveryModelTarget + ''; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Setting database to the ' + @RecoveryModelTarget + ' recovery model was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Setting database to the ' + @RecoveryModelTarget + ' recovery model returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL,'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --================================================-- Get LogFile Status --=================================================-- + SET @LogFileStatus = (SELECT TOP 1 [log_reuse_wait_desc] FROM [sys].[databases] WHERE [NAME] = @DBName); + IF (@LogFileStatus <> 'NOTHING') BEGIN + + --===================================================-- Output result --===================================================-- + SET @return_status = -1; + SET @RunHasErrors = 1; + SET @return_status_text = 'WARN: The log file cannot be shrunk because of the following reason: ' + @LogFileStatus; + PRINT @return_status_text; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===========================================-- Get infos about the log file --============================================-- + PRINT 'INFO: Trying to force DB....'; + SET @SQLCommand = N'USE [' + @DBName + ']; CHECKPOINT;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Setting CHECKPOINT was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Setting CHECKPOINT returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===================================-- Check again if shrinking the log is possible --====================================-- + PRINT 'Waiting for 30 seconds... and retrieving Status again'; + WAITFOR DELAY '00:00:30'; -- Short break for the sync process + SET @LogFileStatus = (SELECT TOP 1 [log_reuse_wait_desc] FROM [sys].[databases] WHERE [NAME] = @DBName); + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: New status of db is: ' + @LogFileStatus; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: New status of db is: ' + @LogFileStatus; + PRINT @return_status_text; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END; + + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --============================================-- Prepare shrinking the log--===============================================-- + IF ((@SHRINKLOG = 1) AND (@LogFileStatus = 'NOTHING')) BEGIN + + --=================================================-- Schrink db logs --===================================================-- + IF ((@CurrentLogFileSizeInMB > @TargetLogFileSizeInMB) AND (@return_status = 0)) BEGIN + + --===================================================-- Now, do it! --=====================================================-- + IF (@return_status = 0) BEGIN + PRINT 'INFO: Shrink the truncated log file to 10% of its current size or the fixed growth increment, whichever is larger.'; + IF (@ProductMainVersion >= 16) BEGIN + IF (@FORCE = 1) BEGIN + SET @SQLCommand = N'USE [' + @DBName + ']; DBCC SHRINKFILE (''' + @LogFileName + ''', ' + CAST(@TargetLogFileSizeInMB AS VARCHAR) + ') WITH WAIT_AT_LOW_PRIORITY (ABORT_AFTER_WAIT = BLOCKERS), NO_INFOMSGS;'; + END; ELSE BEGIN + SET @SQLCommand = N'USE [' + @DBName + ']; DBCC SHRINKFILE (''' + @LogFileName + ''', ' + CAST(@TargetLogFileSizeInMB AS VARCHAR) + ') WITH WAIT_AT_LOW_PRIORITY (ABORT_AFTER_WAIT = SELF), NO_INFOMSGS;'; + END; + END; ELSE BEGIN + PRINT 'WARN: Because of the sql server version, DBCC SHRINKFILE is not applicable with the FORCE parameter!'; + SET @SQLCommand = N'USE [' + @DBName + ']; DBCC SHRINKFILE (''' + @LogFileName + ''', ' + CAST(@TargetLogFileSizeInMB AS VARCHAR) + ') WITH NO_INFOMSGS;'; + END; + + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Shrinking the database log file was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Shrinking the database log file returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + END; ELSE BEGIN + SET @return_status_text = 'ERROR: Shrinking the database log was skipped because of previouse error(s)!'; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END; ELSE BEGIN + PRINT 'INFO: No shrink action neccessary!'; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END; ELSE BEGIN + PRINT 'WARN: Skipping to shrink the log file!'; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --=============================================-- Reset database recovery mode --===========================================-- + IF (@SHRINKLOG = 1) BEGIN + + --=============================================-- Change db recovery mode --===============================================-- + PRINT 'INFO: Reset the database recovery model back to: ' + @RecoveryModelActual + '.'; + SET @SQLCommand = N'ALTER DATABASE [' + @DBName + '] SET RECOVERY ' + @RecoveryModelActual + ''; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Setting database back to the ' + @RecoveryModelActual + ' recovery model was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Setting database back to the ' + @RecoveryModelActual + ' recovery model returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + END; + ----------------------------------------------------------------------------------------------------------------------------- + + + --=========================================-- Check database file growth --===========================================-- + IF ((@CHECKFILEGROWTH = 1) and (@return_status = 0)) BEGIN + + PRINT 'INFO: Checking database file growth settings.'; + + BEGIN TRY + + BEGIN TRY + DROP TABLE #FileGrowthList; + END TRY BEGIN CATCH + PRINT 'INFO: #FileGrowthList Temp table is already gone...'; + END CATCH; + + CREATE TABLE #FileGrowthList (FileName NVARCHAR(128), FileType NVARCHAR(10), FileSizeInMB INT, GrowthDesc NVARCHAR(50), MaxSizeInMB INT, IsPercentGrowth BIT); + + INSERT INTO #FileGrowthList(FileName, FileType, FileSizeInMB, GrowthDesc, MaxSizeInMB, IsPercentGrowth) + SELECT [name], + [type_desc], + ([size] / 128), + CASE WHEN [is_percent_growth] = 1 THEN CAST([growth] AS NVARCHAR(20)) + '%' + ELSE CAST(([growth] / 128) AS NVARCHAR(20)) + ' MB' END, + CASE WHEN [max_size] = -1 THEN -1 ELSE ([max_size] / 128) END, + [is_percent_growth] + FROM [master].[sys].[master_files] + WHERE [database_id] = DB_ID(@DBName); + + SELECT @FileGrowthCount = COUNT(*) FROM #FileGrowthList; + PRINT 'INFO: Found: ' + CONVERT(VARCHAR,@FileGrowthCount) + ' file(s) to check.'; + + IF (@FileGrowthCount > 0) BEGIN + + DECLARE filegrowth_cursor CURSOR STATIC LOCAL FOR + SELECT FileName, FileType, FileSizeInMB, GrowthDesc, MaxSizeInMB, IsPercentGrowth FROM #FileGrowthList; + + OPEN filegrowth_cursor; + FETCH NEXT FROM filegrowth_cursor INTO @FileName, @FileType, @FileSizeInMB, @FileGrowthDesc, @FileMaxSizeInMB, @FileIsPercentGrowth; + WHILE @@FETCH_STATUS = 0 BEGIN + BEGIN TRY + + SET @FileMaxSizeText = CASE WHEN (@FileMaxSizeInMB < 0) THEN 'UNLIMITED' ELSE CONVERT(NVARCHAR(20),@FileMaxSizeInMB) END; + SET @return_status_text = 'INFO: File [' + @FileName + '] (' + @FileType + ') SizeMB=' + CONVERT(VARCHAR(20),@FileSizeInMB) + ', Growth=' + @FileGrowthDesc + ', MaxSizeMB=' + @FileMaxSizeText; + PRINT @return_status_text; + + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'filegrowth_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + + IF (@FileIsPercentGrowth = 1) BEGIN + SET @return_status_text = 'WARN: File [' + @FileName + '] uses percent growth (' + @FileGrowthDesc + ').'; + PRINT @return_status_text; + + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'filegrowth_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + END; + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + --================================================-- Log error to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'filegrowth_cursor', @DBName, ERROR_MESSAGE(),@MY_PROCEDURE_NAME, GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + END CATCH; + + FETCH NEXT FROM filegrowth_cursor INTO @FileName, @FileType, @FileSizeInMB, @FileGrowthDesc, @FileMaxSizeInMB, @FileIsPercentGrowth; + + END; + + CLOSE filegrowth_cursor; + DEALLOCATE filegrowth_cursor; + + END; ELSE BEGIN + PRINT 'INFO: No database files found to check.'; + END; + + BEGIN TRY + DROP TABLE #FileGrowthList; + END TRY BEGIN CATCH + PRINT 'INFO: #FileGrowthList Temp table is already gone...'; + END CATCH; + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + --================================================-- Log error to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'filegrowth', 'check', ERROR_MESSAGE(),@MY_PROCEDURE_NAME,GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + END CATCH; + + END ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping database file growth checks!'; + PRINT @return_status_text; + END; + END; + ------------------------------------------------------------------------------------------------------------------------------ + + --==============================================-- Change db working mode --===============================================-- + IF (@FORCE = 1) BEGIN -- and ignore last result + PRINT 'INFO: Set the database back to MULTI_USER mode'; + SET @SQLCommand = N'ALTER DATABASE [' + @DBName + '] SET MULTI_USER; ALTER DATABASE [' + @DBName + '] SET ONLINE;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Setting the database to MULTI_USER mode was successfully completed!'; + PRINT @return_status_text; + END ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Setting the database to MULTI_USER mode returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + END ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping to change the database mode (FORCE = OFF)!'; + PRINT @return_status_text; + END; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'db_cursor', @DBName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + -- Ensure temp table is droped and cursor is closed + BEGIN + BEGIN TRY + DROP TABLE #DataFileList; + END TRY BEGIN CATCH + PRINT 'INFO: #DataFileList Temp table are already gone...'; + END CATCH; + + IF (@REBUILDHEAPS = 1) BEGIN + + BEGIN TRY + DROP TABLE #HeapList; + END TRY BEGIN CATCH + PRINT 'INFO: #HeapList Temp table are already gone...(1)'; + END CATCH; + + BEGIN TRY + CLOSE heap_cursor; + DEALLOCATE heap_cursor; + END TRY BEGIN CATCH + PRINT 'INFO: Heap cursor is already closed and deallocated...(1)'; + END CATCH; + + END; + + IF (@MAINTAINCOLUMNSTORE = 1) BEGIN + + BEGIN TRY + DROP TABLE #ColumnstoreList; + END TRY BEGIN CATCH + PRINT 'INFO: #ColumnstoreList Temp table are already gone...(1)'; + END CATCH; + + BEGIN TRY + CLOSE columnstore_cursor; + DEALLOCATE columnstore_cursor; + END TRY BEGIN CATCH + PRINT 'INFO: Columnstore cursor is already closed and deallocated...(1)'; + END CATCH; + + END; + + IF (@CHECKFILEGROWTH = 1) BEGIN + + BEGIN TRY + DROP TABLE #FileGrowthList; + END TRY BEGIN CATCH + PRINT 'INFO: #FileGrowthList Temp table are already gone...'; + END CATCH; + + BEGIN TRY + CLOSE filegrowth_cursor; + DEALLOCATE filegrowth_cursor; + END TRY BEGIN CATCH + PRINT 'INFO: File growth cursor is already closed and deallocated...'; + END CATCH; + + END; + + IF (@REBUILDINDEX = 1) BEGIN + + BEGIN TRY + DROP TABLE #TableList; + END TRY BEGIN CATCH + PRINT 'INFO: #TableList Temp table are already gone...(1)'; + END CATCH; + + BEGIN TRY + DROP TABLE #IndexList; + END TRY BEGIN CATCH + PRINT 'INFO: #IndexList Temp table are already gone...(1)'; + END CATCH; + + END; + + IF (@RECOMPILEPROCEDURES = 1) BEGIN + + BEGIN TRY + DROP TABLE #ProcedureList; + END TRY BEGIN CATCH + PRINT 'INFO: #ProcedureList Temp table are already gone...(1)'; + END CATCH; + + BEGIN TRY + CLOSE procedure_cursor; + DEALLOCATE procedure_cursor; + END TRY BEGIN CATCH + PRINT 'INFO: Procedure cursor is already closed and deallocated...(1)'; + END CATCH; + + END; + + END; + + PRINT '-------------------------------'; + + FETCH NEXT FROM db_cursor INTO @DBName; + + END; + + CLOSE db_cursor; + DEALLOCATE db_cursor; + + END + ELSE BEGIN + + SET @return_status = 0; + SET @return_status_text = 'So there is nothing to do...'; + PRINT @return_status_text; + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN','ERROR')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('WARN', 'db_list', 'count', @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --==============================================-- Clear system cache --================================================-- + IF ((@FREESYSTEMCACHE = 1) and (@return_status = 0)) BEGIN + + PRINT 'INFO: Clearing SQL Server system cache.'; + SET @SQLCommand = N'DBCC FREESYSTEMCACHE (''ALL'');'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Clearing SQL Server system cache was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Clearing SQL Server system cache returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + END; ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping SQL Server system cache clearing!'; + PRINT @return_status_text; + END; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'system_cache', 'clear', @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --==============================================-- Clear session cache --================================================-- + IF ((@FREESESSIONCACHE = 1) and (@return_status = 0)) BEGIN + + PRINT 'INFO: Clearing SQL Server session cache.'; + SET @SQLCommand = N'DBCC FREESESSIONCACHE;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Clearing SQL Server session cache was successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Clearing SQL Server session cache returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + END; ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping SQL Server session cache clearing!'; + PRINT @return_status_text; + END; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --===============================================-- Log result to table --=================================================-- + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'session_cache', 'clear', @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --==============================================-- Shrink tempdb --================================================-- + IF ((@SHRINKTEMPDB = 1) and (@return_status = 0)) BEGIN + + PRINT 'INFO: Shrink tempdb data and log files.'; + + IF OBJECT_ID('tempdb..#TempdbFileList') IS NOT NULL DROP TABLE #TempdbFileList; + + CREATE TABLE #TempdbFileList (FileName NVARCHAR(128)); + + INSERT INTO #TempdbFileList(FileName) + SELECT [name] + FROM [tempdb].[sys].[database_files] + WHERE [type_desc] IN ('ROWS','LOG'); + + SELECT @TempdbFileCount = COUNT(*) FROM #TempdbFileList; + PRINT 'INFO: Found: ' + CONVERT(VARCHAR,@TempdbFileCount) + ' tempdb file(s) to shrink.'; + + IF (@TempdbFileCount > 0) BEGIN + + BEGIN TRY + DECLARE tempdb_cursor CURSOR STATIC LOCAL FOR + SELECT FileName FROM #TempdbFileList; + + OPEN tempdb_cursor; + FETCH NEXT FROM tempdb_cursor INTO @TempdbFileName; + WHILE @@FETCH_STATUS = 0 BEGIN + + SET @SQLCommand = N'USE [tempdb]; DBCC SHRINKFILE (N''' + @TempdbFileName + ''', 0) WITH NO_INFOMSGS;'; + EXEC @return_status = sp_executesql @SQLCommand; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Shrinking tempdb file [' + @TempdbFileName + '] was successfully completed!'; + PRINT @return_status_text; + END ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Shrinking tempdb file [' + @TempdbFileName + '] returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'tempdb_cursor', @TempdbFileName, @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + + FETCH NEXT FROM tempdb_cursor INTO @TempdbFileName; + END; + + CLOSE tempdb_cursor; + DEALLOCATE tempdb_cursor; + END TRY + BEGIN CATCH + SET @RunHasErrors = 1; + SET @return_status = -1; + IF CURSOR_STATUS('local', 'tempdb_cursor') >= -1 BEGIN + IF CURSOR_STATUS('local', 'tempdb_cursor') >= 0 CLOSE tempdb_cursor; + DEALLOCATE tempdb_cursor; + END; + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'tempdb', 'shrink', ERROR_MESSAGE(), @MY_PROCEDURE_NAME, GetDate()); + END; + END CATCH; + + END ELSE BEGIN + PRINT 'INFO: No tempdb files found to shrink.'; + END; + + IF OBJECT_ID('tempdb..#TempdbFileList') IS NOT NULL DROP TABLE #TempdbFileList; + + END ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping shrinking tempdb!'; + PRINT @return_status_text; + END; + END; + ----------------------------------------------------------------------------------------------------------------------------- + + --==============================================-- Cleanup maintenance log table --================================================-- + IF ((@CLEANLOGS = 1) and (@return_status = 0)) BEGIN + + IF (@LOGRETENTIONDAYS > 0) BEGIN + + IF EXISTS (SELECT * FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = N'TBDD_MAINTAIN_DATABASE_LOG') BEGIN + + PRINT 'INFO: Cleanup maintenance log entries older than ' + CONVERT(VARCHAR,@LOGRETENTIONDAYS) + ' day(s).'; + + BEGIN TRY + + DELETE FROM [dbo].[TBDD_MAINTAIN_DATABASE_LOG] + WHERE [ADDED_WHEN] < DATEADD(day, -@LOGRETENTIONDAYS, GETDATE()); + + SET @return_status = 0; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Maintenance log cleanup successfully completed! Deleted: ' + CONVERT(VARCHAR,@@ROWCOUNT); + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Maintenance log cleanup returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'log_cleanup', 'cleanup', @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + --================================================-- Log error to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'log_cleanup', 'cleanup', ERROR_MESSAGE(),@MY_PROCEDURE_NAME,GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + END CATCH; + + END ELSE BEGIN + PRINT 'WARN: Log table does not exist, skipping cleanup!'; + END; + + END ELSE BEGIN + PRINT 'INFO: Log retention days is 0, skipping cleanup.'; + END; + + END ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping maintenance log cleanup!'; + PRINT @return_status_text; + END; + END; + ------------------------------------------------------------------------------------------------------------------------------ + + --==============================================-- Cycle SQL Server error logs --================================================-- + IF ((@CYCLEERRORLOGS = 1) and (@return_status = 0)) BEGIN + + BEGIN TRY + + PRINT 'INFO: Cycling SQL Server error log.'; + EXEC @return_status = [master].[dbo].[sp_cycle_errorlog]; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Cycling SQL Server error log successfully completed!'; + PRINT @return_status_text; + END ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Cycling SQL Server error log returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'errorlog', 'cycle', @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + --================================================-- Log error to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'errorlog', 'cycle', ERROR_MESSAGE(),@MY_PROCEDURE_NAME,GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + END CATCH; + + BEGIN TRY + + PRINT 'INFO: Cycling SQL Server Agent error log.'; + EXEC @return_status = [msdb].[dbo].[sp_cycle_agent_errorlog]; + + IF ((@LOGLEVEL in ('INFO')) and (@return_status = 0)) BEGIN + SET @return_status_text = 'INFO: Cycling SQL Server Agent error log successfully completed!'; + PRINT @return_status_text; + END; ELSE IF ((@LOGLEVEL in ('WARN')) and (@return_status <> 0)) BEGIN + SET @return_status_text = 'WARN: Cycling SQL Server Agent error log returns warnings or has failed, check the ID!'; + PRINT @return_status_text; + END; + + IF ((@LOGLEVEL in ('INFO','WARN')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'agent_errorlog', 'cycle', @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + + END TRY BEGIN CATCH + + -- Handle any errors that occur during the process. + SELECT ERROR_MESSAGE() AS ErrorMessage; + SET @RunHasErrors = 1; + SET @return_status = -1; + + --================================================-- Log error to table --=================================================-- + IF (@LOGLEVEL in ('INFO','WARN','ERROR')) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [ADDED_WHO], [ADDED_WHEN]) + VALUES ('ERROR', 'agent_errorlog', 'cycle', ERROR_MESSAGE(),@MY_PROCEDURE_NAME,GetDate()); + END; + ------------------------------------------------------------------------------------------------------------------------------ + + END CATCH; + + END; ELSE BEGIN + IF (@LOGLEVEL in ('INFO')) BEGIN + SET @return_status_text = 'INFO: Skipping error log rotation!'; + PRINT @return_status_text; + END; + END; + ------------------------------------------------------------------------------------------------------------------------------ + + SET @return_status_text = 'END PROCEDURE [' + @MY_PROCEDURE_NAME + '] @ ' + CONVERT(varchar(50),GETDATE(),120); + + IF (@return_status <> 0) BEGIN + SET @RunHasErrors = 1; + END; + + IF ((@LOGLEVEL in ('INFO','WARN','ERROR')) and EXISTS ( + SELECT 1 + FROM [dbo].[TBDD_MAINTAIN_DATABASE_LOG] + WHERE [LOG_LEVEL] = 'ERROR' + AND [ADDED_WHO] = @MY_PROCEDURE_NAME + AND [ADDED_WHEN] >= @RunStart + )) BEGIN + SET @RunHasErrors = 1; + END; + + IF (@RunHasErrors = 1) BEGIN + SET @return_status = -1; + END; + + --================================================-- Log end to table --===================================================-- + IF ((@LOGLEVEL in ('INFO')) and (@return_status is not null) and (@return_status_text is not null)) BEGIN + INSERT INTO [TBDD_MAINTAIN_DATABASE_LOG]([LOG_LEVEL],[MESSAGE1], [MESSAGE2], [MESSAGE3], [MESSAGE4], [ADDED_WHO], [ADDED_WHEN]) + VALUES (@LOGLEVEL, 'closing', 'procedure', @return_status, @return_status_text, @MY_PROCEDURE_NAME, GetDate()); + END; + ----------------------------------------------------------------------------------------------------------------------------- + + PRINT ''; + PRINT @return_status_text; + PRINT '===================================================================================================='; + + Return @return_status; + +END; + +-- ================================================================= +-- Example execution (defaults) +-- ================================================================= +-- EXEC [dbo].[PRDD_MAINTAIN_DATABASE] +-- @pCHECKDB = 0, +-- @pSHRINKLOG = 0, +-- @pSHRINKDB = 0, +-- @pSHRINKTEMPDB = 0, +-- @pREBUILDINDEX = 0, +-- @pRECOMPILEPROCEDURES = 0, +-- @pCLEARQUERYCACHE = 0, +-- @pFREESYSTEMCACHE = 0, +-- @pFREESESSIONCACHE = 0, +-- @pCLEARDBPROCCACHE = 0, +-- @pUPDATESTATISTICS = 1, +-- @pREBUILDHEAPS = 0, +-- @pMAINTAINCOLUMNSTORE = 0, +-- @pCHECKFILEGROWTH = 1, +-- @pCLEANLOGS = 0, +-- @pLOGRETENTIONDAYS = 30, +-- @pCYCLEERRORLOGS = 0, +-- @pFORCE = 0, +-- @pINCLUDEDB = 'DD_ECM', +-- @pEXCLUDEDB = 'master,model,msdb,tempdb', +-- @pLOGLEVEL = 'ERROR';