Print ''
Print '-------------------------------------------------------------------------'
Print ' DB Maintain...: Database Optimice'
Print ' #New..........: 22.03.2006/J.Feuerstein'
Print ' #LUp..........: 18.04.2024/J.Feuerstein'
Print ' #LUp..........: 30.07.2024/J.Feuerstein enhance update Statistics'
Print ' ReOrg.Indecis.: DB:#DB_N#'
Print ' Upd.Statistics: DB:#DB_N#'
Print '-------------------------------------------------------------------------'

GO

USE [#DB_N#]


/*
Script Source:
http://blog.sqlauthority.com/2007/06/24/sql-server-comparison-index-fragmentation-index-de-fragmentation-index-rebuild-sql-server-2000-and-sql-server-2005/

This is a the latest version of this script as of 2008-11-12:
Script originally authored by Microsoft but had an error - It was retrieving data without prefixing database schema.

-Added a @ViewOnly variable that will allow you to run this script as a test only and review proposed actions. Simply change the value to 0 to execute the proposed actions.
-Also enhanced to perform either a reorg or rebuild, based on range of fragmentation value.
-Also included pages value from DBCC SHOWCONTIG for analysis, to support theory that defragging indexes have few pages is not productive.
~Paul DeBrino : infinityrd.com : Nov 2008

USEFUL INFO -- INSPECT INDEXES' FILL FACTOR VALUES:
============================================
use YourDBName
select * from sys.indexes order by name

USEFUL INFO -- VIEW THE DEFAULT FILL FACTOR OF YOUR SERVER:
============================================
use YourDBname
exec sp_configure 'show advanced options', 1
-- go
reconfigure
-- go
exec sp_configure 'fill factor'
-- go
*/

/*
 #Doc: Info 30.07.2024/A.Becker
 https://ola.hallengren.com/sql-server-index-and-statistics-maintenance.html
*/

-- Declare variables:
SET NOCOUNT ON
DECLARE @tablename VARCHAR(128)
DECLARE @tablenameLast VARCHAR(128)
-- #LUp: 18.04.2024/J.Fes set value to max
DECLARE @PrintMsg VARCHAR(max)
DECLARE @execstr VARCHAR(max)
DECLARE @objectid INT
DECLARE @indexid INT
DECLARE @frag decimal
DECLARE @maxreorg decimal
DECLARE @maxrebuild decimal
DECLARE @IdxName varchar(128)
DECLARE @pages INT
DECLARE @maxpages INT
DECLARE @ViewOnly bit
DECLARE @ReorgOptions varchar(255)
DECLARE @RebuildOptions varchar(255)

declare @Optimice_DateTimeStart datetime
declare @Optimice_DateTimeEnd datetime

declare @IndexOptimice_DateTimeStart datetime
declare @IndexOptimice_DateTimeEnd datetime

declare @StatisticUpd_DateTimeStart datetime
declare @StatisticUpd_DateTimeEnd datetime

set @Optimice_DateTimeStart = GetDate()
set @IndexOptimice_DateTimeStart = GetDate()

-- Set to 1 to view proposed actions, set to 0 to Execute proposed actions:
SET @ViewOnly=0

-- Decide on the maximum number of pages to consider index for reorg or rebuild:
-- NOTE: Set to 0 if you wish not to consider this value for analysis.
-- Reference http://sqlblog.com/blogs/kalen_delaney/archive/2008/02/28/fragmentation-revisited.aspx
SET @maxpages = 100
-- Decide on the maximum fragmentation to allow for a reorganize:
-- AVAILABLE OPTIONS: http://technet.microsoft.com/en-us/library/ms188388(SQL.90).aspx
SET @maxreorg = 20.0 -- war default
SET @maxreorg = 5.0
SET @ReorgOptions = 'LOB_COMPACTION=ON'
-- Decide on the maximum fragmentation to allow for a rebuild:
SET @maxrebuild = 30.0 -- war default
SET @maxrebuild = 5.0
-- NOTE: only specifiy FILLFACTOR=x if x is a value other than 0 (otherwise do not include the fillfactor option at all):
SET @RebuildOptions = 'PAD_INDEX=OFF, FILLFACTOR=80, SORT_IN_TEMPDB=ON, STATISTICS_NORECOMPUTE=OFF, ALLOW_ROW_LOCKS=ON, ALLOW_PAGE_LOCKS=ON'

set @PrintMsg = '-------------------------------------------------------------------------'
RAISERROR (@PrintMsg,0,1) WITH NOWAIT

-- Declare a cursor to process list of tables_cursor:
DECLARE tables_cursor CURSOR FOR
SELECT CAST(TABLE_SCHEMA AS VARCHAR(100))+ '.' + CAST(TABLE_NAME AS VARCHAR(100)) AS Table_Name
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
order by TABLE_NAME


-- Create the temporary table:
if exists (select name from tempdb.dbo.sysobjects where name like '#fraglist%')
drop table #fraglist

CREATE TABLE #fraglist (
  ObjectName CHAR(255),
  ObjectId INT,
  IndexName CHAR(255),
  IndexId INT,
  Lvl INT,
  CountPages INT,
  CountRows INT,
  MinRecSize INT,
  MaxRecSize INT,
  AvgRecSize INT,
  ForRecCount INT,
  Extents INT,
  ExtentSwitches INT,
  AvgFreeBytes INT,
  AvgPageDensity INT,
  ScanDensity decimal,
  BestCount INT,
  ActualCount INT,
  LogicalFrag decimal,
  ExtentFrag decimal)

-- Open the cursor:
OPEN tables_cursor
-- Loop through all the tables_cursor in the database:
FETCH NEXT FROM tables_cursor INTO @tablename

 set @PrintMsg = 'Get Index Status for Tables:'
 RAISERROR (@PrintMsg,0,1) WITH NOWAIT

WHILE @@FETCH_STATUS = 0
BEGIN
 set @PrintMsg = ' |-> Table:' + RTRIM(@tablename)
 RAISERROR (@PrintMsg,0,1) WITH NOWAIT
 -- Perform showcontig on all indexes for the current table:
 INSERT INTO #fraglist
   EXEC ('DBCC SHOWCONTIG (''' + @tablename + ''') WITH FAST, TABLERESULTS, ALL_INDEXES, NO_INFOMSGS')
 -- #get next table name
 FETCH NEXT FROM tables_cursor INTO @tablename
END

-- Close and deallocate the cursor:
CLOSE tables_cursor
-- DEALLOCATE tables_cursor

-- Declare the cursor for the list of indexes to be defragged:
DECLARE indexes CURSOR FOR
SELECT ObjectName, ObjectId, IndexId, LogicalFrag, IndexName, CountPages
 FROM #fraglist
 WHERE INDEXPROPERTY (ObjectId, IndexName, 'IndexDepth') > 0
order by ObjectName, IndexName

-- Open the cursor:
OPEN indexes

-- Loop through the indexes:
FETCH NEXT
FROM indexes
INTO @tablename, @objectid, @indexid, @frag, @IdxName, @pages

set @PrintMsg = '-- #Do: Index Optimice --------------------------------------------------'
RAISERROR (@PrintMsg,0,1) WITH NOWAIT
set @tablenameLast=''
WHILE @@FETCH_STATUS = 0
 BEGIN

 if not @tablenameLast=@tablename
  begin
   PRINT '-------------------------------------------------------------------------'
   set @PrintMsg = 'Check Index for Table:[' + RTRIM(@tablename)  + ']'
   RAISERROR (@PrintMsg,0,1) WITH NOWAIT
  end
 set @tablenameLast=@tablename

 -- Determine if index page count surpasses the defined threshold for needed maintenance:
 IF (@pages >= @maxpages)
  BEGIN
  -- Determine if fragmentation surpasses the defined threshold for rebuilding:
  IF (@frag >= @maxrebuild)
   BEGIN
    IF (@ViewOnly=1)
     BEGIN
      PRINT ' |-> Idx:[' + RTRIM(@IdxName) + '] WOULD be exec ALTER INDEX REBUILD WITH ( ' + @RebuildOptions + ' ) -- Frag.cur:' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '(Per cent) and Pages were ' + RTRIM(CONVERT(VARCHAR(15),@pages))
      RAISERROR (@PrintMsg,0,1) WITH NOWAIT
     END
    ELSE
     BEGIN
      set @PrintMsg = ' |-> Idx:[' + RTRIM(@IdxName) + '] Now exec ALTER INDEX REBUILD WITH ( ' + @RebuildOptions + ' ) -- Frag.cur:' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '(Per cent) and Pages were ' + RTRIM(CONVERT(VARCHAR(15),@pages))
      RAISERROR (@PrintMsg,0,1) WITH NOWAIT
      SELECT @execstr = 'ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REBUILD WITH ( ' + @RebuildOptions + ' )'
      EXEC (@execstr)
     END
   END
  -- Determine if fragmentation surpasses the defined threshold for reorganizing:
  ELSE IF (@frag >= @maxreorg)
   BEGIN
    IF (@ViewOnly=1)
     BEGIN
      set @PrintMsg = ' |-> Idx:[' + RTRIM(@IdxName) + '] WOULD be exec ALTER INDEX REORGANIZE WITH ( ' + @ReorgOptions + ' ) -- Frag.cur:' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '(Per cent) and Pages were ' + RTRIM(CONVERT(VARCHAR(15),@pages))
      RAISERROR (@PrintMsg,0,1) WITH NOWAIT
     END
    ELSE
     BEGIN
      set @PrintMsg = ' |-> Idx:[' + RTRIM(@IdxName) + '] Now exec ALTER INDEX REORGANIZE WITH ( ' + @ReorgOptions + ' ) -- Frag.cur:' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '(Per cent) and Pages were ' + RTRIM(CONVERT(VARCHAR(15),@pages))
      RAISERROR (@PrintMsg,0,1) WITH NOWAIT
      SELECT @execstr = 'ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REORGANIZE WITH ( ' + @ReorgOptions + ' )'
      EXEC (@execstr)
     END
   END
  ELSE
   BEGIN
    -- PRINT ' |-> BYPASSING INDEX since Frag.cur:' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '% which is less than the Reorg and Rebuild thresholds.'
    set @PrintMsg = ' |-> Idx:[' + RTRIM(@IdxName) + '] Nothing to do'
    RAISERROR (@PrintMsg,0,1) WITH NOWAIT
   END
  END
  ELSE
   BEGIN
    -- PRINT ' |-> BYPASSING INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' since Page Count was ' + RTRIM(CONVERT(VARCHAR(15),@pages)) + ' which is less than threshold. Frag.cur:' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '%'
    set @PrintMsg = ' |-> Idx:[' + RTRIM(@IdxName) + '] Nothing to do'
    RAISERROR (@PrintMsg,0,1) WITH NOWAIT
   END
  FETCH NEXT
  FROM indexes
  INTO @tablename, @objectid, @indexid, @frag, @IdxName, @pages
 END

-- Close and deallocate the cursor:
CLOSE indexes
DEALLOCATE indexes

-- Delete the temporary table:
DROP TABLE #fraglist
set @IndexOptimice_DateTimeEnd=GetDate()

set @PrintMsg = ''
RAISERROR (@PrintMsg,0,1) WITH NOWAIT
RAISERROR (@PrintMsg,0,1) WITH NOWAIT
set @PrintMsg = '-- #Do: Update Statistic ------------------------------------------------'
RAISERROR (@PrintMsg,0,1) WITH NOWAIT

set @StatisticUpd_DateTimeStart = GetDate()
-- Open the tables_cursor:
OPEN tables_cursor

-- Loop through all the tables_cursor in the database:
FETCH NEXT
FROM tables_cursor
INTO @tablename

WHILE @@FETCH_STATUS = 0
BEGIN
 SET @execstr = 'UPDATE STATISTICS '  + @tablename + ' WITH FULLSCAN'   -- BSH 5 min
 -- SET @execstr = 'UPDATE STATISTICS '  + @tablename + ' WITH SAMPLE 50 PERCENT' -- BSH 9 min
 -- SET @execstr = 'UPDATE STATISTICS '  + @tablename
 set @PrintMsg = 'Update Statistic for Table:[' + RTRIM(@tablename) + ']'
 RAISERROR (@PrintMsg,0,1) WITH NOWAIT
 set @PrintMsg = ' |-> via:' + @execstr
 RAISERROR (@PrintMsg,0,1) WITH NOWAIT
 IF (@ViewOnly=0)
  begin
   EXEC (@execstr)
  end
 -- #Doc: Get next table name
 FETCH NEXT FROM tables_cursor INTO @tablename
END
set @StatisticUpd_DateTimeEnd=GetDate()

-- Close and deallocate the cursor:
CLOSE tables_cursor
DEALLOCATE tables_cursor

-- #Doc: Print Durations: ----------------------------------------------------------------------------------
PRINT '-------------------------------------------------------------------------'
Execute JF_ShowTimeDiffToNow 'Optimise:Index DB..:#DB_N#', @IndexOptimice_DateTimeStart, @IndexOptimice_DateTimeEnd
PRINT '-------------------------------------------------------------------------'
Execute JF_ShowTimeDiffToNow 'Update Statistic DB:#DB_N#', @StatisticUpd_DateTimeStart, @StatisticUpd_DateTimeEnd
PRINT '-------------------------------------------------------------------------'
set @Optimice_DateTimeEnd = GetDate()
Execute JF_ShowTimeDiffToNow 'Entire duration DB.:#DB_N#', @Optimice_DateTimeStart, @Optimice_DateTimeEnd

Print ''
Print '-------------------------------------------------------------------------'
Print ' End execute SQL Script: JobMgr_61_DataBaseOptimice_ReorgIndices.sql'
Print '-------------------------------------------------------------------------'
GO

