Below is a script that will run against every table in every database on your Sql Server instance and output, among other things, the average index fragmentation percentage.
It can be a bit heavy when running against large databases with thousands of tables so I would recommend running it during downtime or outside office hours.
This script returns fragmentation stats by utilizing a DMV.
When Nulls are presented as parameters this DMV works at a server level and uses preset defaults.
[sys].[dm_db_index_physical_stats](NULL, NULL, NULL, NULL, NULL)
However it also accepts the parameter options below to limited the results returned.
[sys].[dm_db_index_physical_stats](Database_Id, Object_Id, Index_Id, partition_number, MODE)
MODE OPTIONS:
Mode specifies the scan level that is used to obtain statistics. Valid inputs are DEFAULT, NULL, LIMITED, SAMPLED, or DETAILED. The default (NULL) is LIMITED.
DEFAULT will have 12 columns full of data and then NULLS in the remainder.
SAMPLED will have 21 columns full of data.
LIMITED will have 12 columns of data and the NULLS in the remainder.
DETAILED will have 21 columns full of data.
Note: I’ve capture more fields in the temp table #Index than I use in the final select statement. This is because you might want to include additional index related fields i.e. check if the index is unique etc. If all you want is the index name in the final select statement you can tidy up the query as needed and exclude the unwanted additional fields.
SET NOCOUNT ON
IF OBJECT_ID('tempdb..#Schema') IS NOT NULL
DROP TABLE #Schema
IF OBJECT_ID('tempdb..#Index') IS NOT NULL
DROP TABLE #Index
DECLARE @Sql AS VARCHAR(MAX)
DECLARE @Database TABLE (
DbId INT
,DbName SYSNAME
)
DECLARE @DbName AS SYSNAME
DECLARE @DbId AS INT
CREATE TABLE #Schema (
DbId INT
,DbName SYSNAME
,Object_Id INT NULL
,ObjectName SYSNAME NULL
,TableFlag BIT
,ViewFlag BIT
,Schema_Id INT NULL
,SchemaName SYSNAME NULL
)
CREATE TABLE #Index (
DbId INT
,DbName SYSNAME
,Object_Id INT NOT NULL
,NAME SYSNAME NULL
,index_id INT NOT NULL
,type TINYINT NOT NULL
,type_desc NVARCHAR(60) NULL
,is_unique BIT NULL
,data_space_id INT NULL
,ignore_dup_key BIT NULL
,is_primary_key BIT NULL
,is_unique_constraint BIT NULL
,fill_factor TINYINT NOT NULL
,is_padded BIT NULL
,is_disabled BIT NULL
,is_hypothetical BIT NULL
,allow_row_locks BIT NULL
,allow_page_locks BIT NULL
,has_filter BIT NULL
,filter_definition NVARCHAR(max) NULL
)
INSERT INTO @Database (
DbId
,DbName
)
SELECT dbid
,NAME
FROM master.dbo.sysdatabases
WHERE NAME <> 'tempdb'
ORDER BY NAME ASC
SET @DbId = 0
WHILE @DbId IS NOT NULL
BEGIN
SET @DbId = (
SELECT MIN(DbId)
FROM @Database
WHERE DbId > @DbId
)
SET @DbName = (
SELECT DbName
FROM @Database
WHERE DbId = @DbId
)
SET @Sql = '
INSERT INTO #Schema (
DbId
,DbName
,Object_Id
,ObjectName
,TableFlag
,ViewFlag
,Schema_Id
,SchemaName
)
SELECT ' + CONVERT(VARCHAR(128), @DbId) + ' AS DbId
,' + '''' + @DbName + '''' + ' AS DbName
,o.Object_Id
,o.NAME AS ObjectName
,CASE
WHEN o.type = ''IT''
THEN 1
WHEN o.type = ''S''
THEN 1
WHEN o.type = ''TT''
THEN 1
WHEN o.type = ''U''
THEN 1
ELSE NULL
END AS TableFlag
,CASE
WHEN o.type = ''V''
THEN 1
ELSE NULL
END AS ViewFlag
,s.Schema_Id
,s.NAME AS SchemaName
FROM ' + QUOTENAME(@DbName) + '.sys.objects AS o WITH (NOLOCK)
INNER JOIN ' + QUOTENAME(@DbName) + '.sys.schemas AS s WITH (NOLOCK) ON o.schema_id = s.schema_id
'
EXEC (@Sql)
SET @Sql = '
INSERT INTO #Index (
DbId
,DbName
,object_id
,NAME
,index_id
,type
,type_desc
,is_unique
,data_space_id
,ignore_dup_key
,is_primary_key
,is_unique_constraint
,fill_factor
,is_padded
,is_disabled
,is_hypothetical
,allow_row_locks
,allow_page_locks
,has_filter
,filter_definition
)
SELECT ' + CONVERT(VARCHAR(128), @DbId) + ' AS DbId
,' + '''' + @DbName + '''' + ' AS DbName
,object_id
,NAME
,index_id
,type
,type_desc
,is_unique
,data_space_id
,ignore_dup_key
,is_primary_key
,is_unique_constraint
,fill_factor
,is_padded
,is_disabled
,is_hypothetical
,allow_row_locks
,allow_page_locks
,has_filter
,filter_definition
FROM ' + QUOTENAME(@DbName) + '.sys.indexes WITH (NOLOCK)
'
EXEC (@Sql)
END
SELECT ROW_NUMBER() OVER (
ORDER BY s.DbName ASC
,s.SchemaName ASC
,s.ObjectName
) AS Row
,s.DbName
,s.SchemaName AS SchemaName
,s.Object_Id AS ObjectId
,s.ObjectName
,s.TableFlag
,s.ViewFlag
,i.Name AS IndexName
,ddips.index_type_desc AS IndexType
,ddips.page_count AS Pages
,ddips.record_count AS Records
,ddips.fragment_count AS Frags
,ddips.avg_fragmentation_in_percent AS AvgFragPercent
FROM sys.dm_db_index_physical_stats(NULL, NULL, NULL, NULL, 'SAMPLED') AS ddips
INNER JOIN #Schema AS s ON ddips.database_id = s.DbId
INNER JOIN #Index AS i ON s.DbId = i.DbId
AND s.Object_Id = i.Object_Id
AND ddips.index_id = i.index_id
AND ddips.object_id = s.Object_Id
WHERE (
s.TableFlag = 1
OR s.ViewFlag = 1
)
ORDER BY s.DbName ASC
,s.SchemaName ASC
,s.ObjectName
Like this:
Like Loading...