Blog do EzequielPortuguese PFE SQL Server Team
Hello all, because the procedure usp_AdaptiveIndexDefrag has undergone a few more updates, starting now this post will be the only source of information for it, including a full change log at the end of this post. It’s also reachable from the links section on the right anywhere on the blog.
What’s the purpose of this?
The purpose for this procedure to to perform an intelligent defrag on one or more indexes for one or more databases. The 1st release was inspired by an earlier release of Michelle Ufford’s code in SQLFOOL.com site, and has since evolved to suit different and added needs. In a nutshell, this procedure automatically chooses whether to rebuild or reorganize an index according to its fragmentation level, amongst other parameters, like if page locks are allowed or the existence of LOBs. All within a specified time frame you choose, defaulting to 8 hours. The defrag priority can also be set, either on size, fragmentation level or index usage (based on range scan count), which is the default. It also handles partitioned indexes, optional statistics update (table-wide or only those related to indexes), rebuilding with the original fill factor or index padding and online operations, to name a few options.
On what version of SQL can I use it?
This procedure can be used from SQL Server 2005 SP2 onwards, because of the DMVs and DMFs involved.
How to deploy it?
Starting with v1.3.7, on any database context you choose to create the usp_AdaptiveIndexDefrag and its supporting objects, open the attached script, and either change the @deploymode variable at the top to upgrade mode (preserving all historic data), or use the default for new deployments or overwrite old versions and objects (disregarding historic data).
How to use it?
After executing the attached script in a user database of your choice, either run the procedure usp_AdaptiveIndexDefrag with no parameters, since all are optional (If not specified, the defaults for each parameter are used), or customize its use with the following parameters, grouped by type:
@Exec_Print defaults to 1 (that is to execute the SQL code generated by this SP) or optionally 0 (just print the commands). @printCmds defaults to 1 (print all commands to screen) or optionally 0 (do not print commands). Useful if you just want to see what commands would be executed. @outputResults defaults to 1 (output fragmentation information after run completes) or optionally 0 (does not output fragmentation information). @debugMode defaults to 1 (display debug comments) or optionally 0 (do not display debug comments). @timeLimit limits how much time can be spent performing index defrags and is expressed in minutes. Note that the time limit is checked BEFORE an index defrag begins, thus a long index defrag can exceed the time limit. Defaults to 480m (8h). @dbScope specifies a database name to defrag. If not specified, all non-system databases plus msdb and model will be defragmented. @tblName specifies if you only want to defrag indexes for a specific table. The input format is database_name.schema.table_name. If not specified, all tables will be defragmented. @defragOrderColumn defines how to prioritize the order of defrags and is used only if @Exec_Print is set to 1. The default is to order by range_scan_count (count of range and table scans on the index because these can benefit the most from defragmentation), other option are fragmentation (amount of fragmentation in the index) or page_count (number of pages in the index). @defragSortOrder defines the sort order of the ORDER BY clause on the above query on how to prioritize the order of defrags. Options are ASC (ascending) or DESC (descending), which is the default. @forceRescan defaults to 0, where a rescan will not occur until all indexes have been defragmented (this allows for a defrag run to span multiple executions over several periods of time). Other option is 1 to force a rescan. @defragDelay specifies the time to wait between defrag commands and defaults to 5s. Refer to Reorganizing and Rebuilding Indexes for documentation on the following parameters. @ixtypeOption defaults to NULL (all indexes will be defragmented). Other options are 1 (only Clustered indexes) or 0 (only Non-Clustered indexes, including XML and Spatial Indexes). @minFragmentation defaults to 5%, will not defrag if fragmentation is less. @rebuildThreshold defaults to 30%. Higher than 30% will result in a rebuild operations instead of reorganize. @minPageCount specifies how many pages must exist in an index in order to be considered for a defrag. Defaults to one extent (8 pages). @maxPageCount specifies the maximum number of pages that can exist in an index and still be considered for a defrag run. Useful for scheduling small indexes during business hours and large indexes for non-business hours. @fillfactor defaults to 1 (the original FF from when the index was created or last defragmented) or optional 0 (uses the default FF of 0). @scanMode specifies which scan mode to use to determine fragmentation levels. LIMITED mode is the default. Scans the smallest number of pages. For an index, only the parent-level pages of the B-tree (that is, the pages above the leaf level) are scanned. For a heap, only the associated PFS and IAM pages are examined. The data pages of the heap are not scanned. Other options include SAMPLED (returns statistics based on a 1 percent sample of all the pages in the index or heap) or DETAILED (scans all pages and returns all statistics. Can cause performance issues). If the index or heap has fewer than 10,000 pages, DETAILED mode is automatically used instead of SAMPLED. @onlineRebuild defaults to 0 (offline rebuild) or optionally 1 (online rebuild if possible) @sortInTempDB defaults to 0 (perform sort operation in the index's database) or optionally 1 (perform sort operation in TempDB). If a sort operation is not required, or if the sort can be performed in memory, SORT_IN_TEMPDB is ignored. Setting this option to 1 can result in faster defrags and prevent database file size inflation. The caveat is you have to monitor TempDB closely. @maxDopRestriction specifies a processor limit for index rebuilds. If not specified, defrag operations will use the system MaxDOP setting. @updateStats defaults to 1 (updates stats when reorganizing) or optionally 0 (does not update stats when reorganizing) @updateStatsWhere defaults to 1 (updates only index related stats) or optionally 0 (updates all stats in entire table). Even if you choose to update stats, and if the @scanMode option was NOT set in LIMITED mode, only those within certain thresholds will be updated. Refer to Statistical maintenance functionality (autostats) in SQL Server to check the defaults for auto-update statistics. @statsSample defaults to NULL (performs a sample scan on the target table or indexed view where the database engine automatically computes the required sample size), or optionally FULLSCAN (all rows in table or view should be read to gather the statistics) or RESAMPLE (statistics will be gathered using an inherited sampling ratio for all existing statistics including indexes). @ix_statsnorecomp defaults to 0 (run with STATISTICS_NORECOMPUTE OFF). Refer to ALTER INDEX (Transact-SQL) for information on the option STATISTICS_NORECOMPUTE. Optionally use 1 (run with STATISTICS_NORECOMPUTE ON will disable the auto update statistics on index related statistics). If you are dealing with stats update with a custom job (or even with this code by updating statistics), you may use this option. @dealMaxPartition specifies whether to exclude the right-most populated partition (if an index is partitioned), or act only on that same partition, excluding all others. Typically, this is the partition that is currently being written to in a sliding-window scenario. Enabling this feature may reduce contention. This may not be applicable in other types of partitioning scenarios. Non-partitioned indexes are unaffected by this option. This parameter defaults to 0 (only right-most populated partition is defragmented). If the partition is smaller than @minPageCount, it won't be considered. Other options are 1 (to exclude the right-most populated partition) or NULL (all partitions are defragmented). @dealLOB specifies if all pages that contain large object (LOB) data are compacted or not. The LOB data types are image, text, ntext, varchar(max), nvarchar(max), varbinary(max), and xml. Compacting this data can improve disk space use. Reorganizing a specified clustered index compacts all LOB columns that are contained in the clustered index. Reorganizing a non-clustered index compacts all LOB columns that are non-key (included) columns in the index. Default is 0 (compact LOBs when reorganizing) and optional 1 (does not compact LOBs when reorganizing).
@Exec_Print defaults to 1 (that is to execute the SQL code generated by this SP) or optionally 0 (just print the commands). @printCmds defaults to 1 (print all commands to screen) or optionally 0 (do not print commands). Useful if you just want to see what commands would be executed. @outputResults defaults to 1 (output fragmentation information after run completes) or optionally 0 (does not output fragmentation information). @debugMode defaults to 1 (display debug comments) or optionally 0 (do not display debug comments). @timeLimit limits how much time can be spent performing index defrags and is expressed in minutes. Note that the time limit is checked BEFORE an index defrag begins, thus a long index defrag can exceed the time limit. Defaults to 480m (8h). @dbScope specifies a database name to defrag. If not specified, all non-system databases plus msdb and model will be defragmented. @tblName specifies if you only want to defrag indexes for a specific table. The input format is database_name.schema.table_name. If not specified, all tables will be defragmented. @defragOrderColumn defines how to prioritize the order of defrags and is used only if @Exec_Print is set to 1. The default is to order by range_scan_count (count of range and table scans on the index because these can benefit the most from defragmentation), other option are fragmentation (amount of fragmentation in the index) or page_count (number of pages in the index). @defragSortOrder defines the sort order of the ORDER BY clause on the above query on how to prioritize the order of defrags. Options are ASC (ascending) or DESC (descending), which is the default. @forceRescan defaults to 0, where a rescan will not occur until all indexes have been defragmented (this allows for a defrag run to span multiple executions over several periods of time). Other option is 1 to force a rescan. @defragDelay specifies the time to wait between defrag commands and defaults to 5s.
Refer to Reorganizing and Rebuilding Indexes for documentation on the following parameters. @ixtypeOption defaults to NULL (all indexes will be defragmented). Other options are 1 (only Clustered indexes) or 0 (only Non-Clustered indexes, including XML and Spatial Indexes). @minFragmentation defaults to 5%, will not defrag if fragmentation is less. @rebuildThreshold defaults to 30%. Higher than 30% will result in a rebuild operations instead of reorganize. @minPageCount specifies how many pages must exist in an index in order to be considered for a defrag. Defaults to one extent (8 pages). @maxPageCount specifies the maximum number of pages that can exist in an index and still be considered for a defrag run. Useful for scheduling small indexes during business hours and large indexes for non-business hours. @fillfactor defaults to 1 (the original FF from when the index was created or last defragmented) or optional 0 (uses the default FF of 0). @scanMode specifies which scan mode to use to determine fragmentation levels. LIMITED mode is the default. Scans the smallest number of pages. For an index, only the parent-level pages of the B-tree (that is, the pages above the leaf level) are scanned. For a heap, only the associated PFS and IAM pages are examined. The data pages of the heap are not scanned. Other options include SAMPLED (returns statistics based on a 1 percent sample of all the pages in the index or heap) or DETAILED (scans all pages and returns all statistics. Can cause performance issues). If the index or heap has fewer than 10,000 pages, DETAILED mode is automatically used instead of SAMPLED.
@onlineRebuild defaults to 0 (offline rebuild) or optionally 1 (online rebuild if possible) @sortInTempDB defaults to 0 (perform sort operation in the index's database) or optionally 1 (perform sort operation in TempDB). If a sort operation is not required, or if the sort can be performed in memory, SORT_IN_TEMPDB is ignored. Setting this option to 1 can result in faster defrags and prevent database file size inflation. The caveat is you have to monitor TempDB closely. @maxDopRestriction specifies a processor limit for index rebuilds. If not specified, defrag operations will use the system MaxDOP setting.
@updateStats defaults to 1 (updates stats when reorganizing) or optionally 0 (does not update stats when reorganizing) @updateStatsWhere defaults to 1 (updates only index related stats) or optionally 0 (updates all stats in entire table). Even if you choose to update stats, and if the @scanMode option was NOT set in LIMITED mode, only those within certain thresholds will be updated. Refer to Statistical maintenance functionality (autostats) in SQL Server to check the defaults for auto-update statistics. @statsSample defaults to NULL (performs a sample scan on the target table or indexed view where the database engine automatically computes the required sample size), or optionally FULLSCAN (all rows in table or view should be read to gather the statistics) or RESAMPLE (statistics will be gathered using an inherited sampling ratio for all existing statistics including indexes). @ix_statsnorecomp defaults to 0 (run with STATISTICS_NORECOMPUTE OFF). Refer to ALTER INDEX (Transact-SQL) for information on the option STATISTICS_NORECOMPUTE. Optionally use 1 (run with STATISTICS_NORECOMPUTE ON will disable the auto update statistics on index related statistics). If you are dealing with stats update with a custom job (or even with this code by updating statistics), you may use this option.
@dealMaxPartition specifies whether to exclude the right-most populated partition (if an index is partitioned), or act only on that same partition, excluding all others. Typically, this is the partition that is currently being written to in a sliding-window scenario. Enabling this feature may reduce contention. This may not be applicable in other types of partitioning scenarios. Non-partitioned indexes are unaffected by this option. This parameter defaults to 0 (only right-most populated partition is defragmented). If the partition is smaller than @minPageCount, it won't be considered. Other options are 1 (to exclude the right-most populated partition) or NULL (all partitions are defragmented). @dealLOB specifies if all pages that contain large object (LOB) data are compacted or not. The LOB data types are image, text, ntext, varchar(max), nvarchar(max), varbinary(max), and xml. Compacting this data can improve disk space use. Reorganizing a specified clustered index compacts all LOB columns that are contained in the clustered index. Reorganizing a non-clustered index compacts all LOB columns that are non-key (included) columns in the index. Default is 0 (compact LOBs when reorganizing) and optional 1 (does not compact LOBs when reorganizing).
What objects are created when running the attached script?
A few common usage scenarios for this script:
Change log:
Version
Date
Comments
1
08-02-2011
Initial release;
1.1
15-02-2011
Added support for maintaining current index padding options;Added logic for exclusion of hypothetical objects;Deal with LOB compaction when reorganizing;Corrected bug with update stats kicking in when not supposed to;Corrected options not compatible with partitioned indexes;
1.2
10-03-2011
Increased control over new or changed database handling;
1.2.1
22-03-2011
Corrected method of finding available processors;
1.3
21-06-2011
Added more options to act upon statistics (IX related or Table-wide);Added finer thresholds for updates on table-wide statistics when reorganizing (when SAMPLED or DETAILED scanMode is selected) *;Added option for no_recompute on index REBUILD;Added restrictions for spatial and XML indexes;Always rebuild filtered indexes;If found, output list of disabled or hypothetical indexes so that you can act on them;Added range scan count to logging table for comparison;Added update index related stats (with defaults) before rebuild operations. This provides better cardinality estimation, and thus a more time-efficient operation when rebuilding;Bug fix in Reorganize statements.Bug fix in one Rescanning condition.
1.3.1
28-06-2011
Corrected issue with commands running on multiple partitions.Changed behaviour of update statistics when tables have multiple partitions.
1.3.2
01-07-2011
Changed objects named %Exclusions to %Exceptions. When re-deploying, existing %Exclusions table will be renamed and not recreated.Added procedure to check current batch execution progress (usp_CurrentExecStats).
1.3.3
08-07-2011
Corrected issue where explicit change in database scope parameter did not trigger rescan under certain conditions.Corrected statistics update thresholds.
1.3.4
22-07-2011
Bug fix in indexes information when working in SQL 2005.
1.3.5
15-11-2011
Bug fix in logging showing as NULL on some issued commands. Optimizations on support SP usp_AdaptiveIndexDefrag_Exceptions.
1.3.6
17-02-2012
Allow large object names in tables and indexes.
1.3.7
27-02-2012
Enhanced error reporting view to incorporate stats updates.Bug fix when certain index options were chosen together.
1.3.8
28-02-2012
Corrected view that reports last run. Added upgrade mode.
1.3.9
12-03-2012
Fixed upgrade mode in case old data cannot be copied back.
1.4.0
12-04-2012
Fixed issue with case sensitive servers.
1.4.1
17-05-2012
Fixed issue on support SP usp_AdaptiveIndexDefrag_Exceptions.
* As you may know, the default thresholds for auto update statistics differ. If the cardinality for a table is greater than 6, but less than or equal to 500, update statistics every 500 modifications. If the cardinality for a table is greater than 500, update statistics when (500 + 20 percent of the table) changes have occurred. On the other hand, the sp_updatestats method is only sensitive to the row modification counter calculated in the sys.sysindexes catalog view, which counts the total number of inserted, deleted, or updated rows since the last time statistics were updated for the table. This can be can be over simplistic in itself. So, when executing this script (thru a daily job, for example), and you choose a scan mode option other than ‘Limited’ (pertaining to sys.dm_db_index_physical_stats), the default for this script (refer to the parameters section in the script for other options), some finer thresholds somewhere in between both methods can be forced when dealing with statistics update. One other enhancement is to issue an UPDATE STATISTICS command on the index just before rebuilding it. This can yield better performance in the rebuild phase itself. Does not apply when an index is reorganized instead of rebuilt. You can look in the changelog section of the usp_AdaptiveIndexDefrag for detailed information on the changes done in every version so far.
Until next time!
Disclaimer: I hope that the information on these pages is valuable to you. Your use of the information contained in these pages, however, is at your sole risk. All information on these pages is provided "as -is", without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by Ezequiel. Further, Ezequiel shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.
Hello all, Sorry for the inconvenience, but the content of this post moved to the Adaptive Index Defrag
Great work, Pedro !