SQL SERVER – 2005 – A Simple Way To Defragment All Indexes In A Database That Is Fragmented Above A Declared Threshold

Just a day ago, I received email from regular reader Rajiv Kayasthy about a script which demonstrates the A Simple Way To Defragment All Indexes In A Database That Is Fragmented Above A Declared Threshold. He found this script on TechNet BOL and was attempting to run on SQL Server but was getting continuous error

Msg 2501, Level 16, State 45, Line 1
Cannot find a table or object with the name “TableName”. Check the system catalog.

After looking at the script provided on BOL I found that it has very small error. It was retrieving data without prefixing database schema. I have modified original script and corrected it to run on SQL Server 2005.

Following corrected script demonstrates Simple Way To Defragment All Indexes In A Database That Is Fragmented Above A Declared Threshold.

/* Originally created by Microsoft */
/* Error corrected by Pinal Dave (http://www.SQLAuthority.com) */
-- Specify your Database Name
USE AdventureWorks;
GO
-- Declare variables
SET NOCOUNT ON;
DECLARE @tablename VARCHAR(128);
DECLARE @execstr VARCHAR(255);
DECLARE @objectid INT;
DECLARE @indexid INT;
DECLARE @frag decimal;
DECLARE @maxfrag decimal;
-- Decide on the maximum fragmentation to allow for.
SELECT @maxfrag = 30.0;
-- Declare a cursor.
DECLARE tables 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';
-- Create the table.
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;
-- Loop through all the tables in the database.
FETCH NEXT
FROM tables
INTO @tablename;
WHILE @@FETCH_STATUS = 0
BEGIN;
-- Do the showcontig of all indexes of the table
INSERT INTO #fraglist
EXEC ('DBCC SHOWCONTIG (''' + @tablename + ''')
WITH FAST, TABLERESULTS, ALL_INDEXES, NO_INFOMSGS'
);
FETCH NEXT
FROM tables
INTO @tablename;
END;
-- Close and deallocate the cursor.
CLOSE tables;
DEALLOCATE tables;
-- Declare the cursor for the list of indexes to be defragged.
DECLARE indexes CURSOR FOR
SELECT
ObjectName, ObjectId, IndexId, LogicalFrag
FROM #fraglist
WHERE LogicalFrag >= @maxfrag
AND INDEXPROPERTY (ObjectId, IndexName, 'IndexDepth') > 0;
-- Open the cursor.
OPEN indexes;
-- Loop through the indexes.
FETCH NEXT
FROM indexes
INTO @tablename, @objectid, @indexid, @frag;
WHILE @@FETCH_STATUS = 0
BEGIN;
PRINT 'Executing DBCC INDEXDEFRAG (0, ' + RTRIM(@tablename) + ',
'
+ RTRIM(@indexid) + ') - fragmentation currently '
+ RTRIM(CONVERT(VARCHAR(15),@frag)) + '%';
SELECT @execstr = 'DBCC INDEXDEFRAG (0, ' + RTRIM(@objectid) + ',
'
+ RTRIM(@indexid) + ')';
EXEC (@execstr);
FETCH NEXT
FROM indexes
INTO @tablename, @objectid, @indexid, @frag;
END;
-- Close and deallocate the cursor.
CLOSE indexes;
DEALLOCATE indexes;
-- Delete the temporary table.
DROP TABLE #fraglist;
GO

Reference : Pinal Dave (http://blog.SQLAuthority.com), TechNet BOL

21 thoughts on “SQL SERVER – 2005 – A Simple Way To Defragment All Indexes In A Database That Is Fragmented Above A Declared Threshold

  1. hello

    found you solution helpfull. don’t want to seem ungratefull but i have a few additions:

    1st – i’d check for the existence of the temp table:
    if exists (select name from tempdb.dbo.sysobjects where name like ‘#fraglist%’)
    drop table #fraglist

    2nd – id use the alter index aproach as dbcc indexdefrag is deprecated in 2005 and unsupported in 2008. here si the cod snippet for those lazy as me :) :
    PRINT ‘Executing ALTER INDEX ‘ + RTRIM(@IdxName) + ‘ ON ‘ + RTRIM(@tablename) + ‘REORGANIZE WITH ( LOB_COMPACTION = ON ) – fragmentation currently ‘ + RTRIM(CONVERT(VARCHAR(15),@frag)) + ‘%';
    SELECT @execstr = ‘ALTER INDEX ‘ + RTRIM(@IdxName) + ‘ ON ‘ + RTRIM(@tablename) + ‘ REORGANIZE WITH ( LOB_COMPACTION = ON )’
    EXEC (@execstr);

    instead of
    PRINT ‘Executing DBCC INDEXDEFRAG (0, ‘ + RTRIM(@tablename) + ‘,
    ‘ + RTRIM(@indexid) + ‘) – fragmentation currently ‘
    + RTRIM(CONVERT(VARCHAR(15),@frag)) + ‘%';
    SELECT @execstr = ‘DBCC INDEXDEFRAG (0, ‘ + RTRIM(@objectid) + ‘,
    ‘ + RTRIM(@indexid) + ‘)';
    EXEC (@execstr);

    Like

  2. I Just like to share this… It defrag index with 50% fragment or more then print a report.

    — Index Fragmentation: Delete unused pages
    — ICDB

    Declare @SQL nvarchar(4000),
    @acursor_TableName cursor,
    @TableName varchar(150),
    @acursor_Indexes cursor,
    @Indexes varchar(150)

    Create Table #Report
    (
    [Table Name] varchar(150),
    Fragment decimal(18,2),
    Defragment decimal(18,2),
    Percentage decimal(18,2)
    )

    Create Table #TableIndex
    (
    Indexes varchar(150),
    Fragment decimal(18,2)
    )

    –Start Looping (Get table)
    set @acursor_TableName = cursor for SELECT TABLE_NAME AS Table_Name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = ‘BASE TABLE’ And TABLE_SCHEMA = ‘dbo’
    open @acursor_TableName
    fetch next from @acursor_TableName into @TableName
    while (@@fetch_status = 0)
    begin

    —————————————————————————————————
    INSERT INTO #Report
    Values(@TableName,’0′,’0′,’0′)

    Truncate Table #TableIndex

    Set @SQL = ‘Select Name, Avg_fragmentation_in_Percent
    FROM sys.dm_db_index_physical_stats (db_id(N”ICDB”),
    Object_id(”’ + @TableName + ”’),null,null,null) as A
    JOIN
    sys.indexes as B on a.Object_ID = b.Object_id
    AND a.index_id = b.index_id
    AND name is not null
    AND name ””’

    Insert INTO #TableIndex(Indexes,Fragment)
    Execute sp_Executesql @sql

    UPDATE A SET Fragment = (SELECT Sum(Fragment) / (Select Count(*) FROM #TableIndex) FROM #TableIndex)
    FROM #Report A
    Where [Table Name] = @TableName

    –Start Looping (Get Indexes)
    set @acursor_Indexes = cursor for SELECT Indexes FROM #TableIndex Where Fragment >= 50
    open @acursor_Indexes
    fetch next from @acursor_Indexes into @Indexes
    while (@@fetch_status = 0)
    begin

    —————————————————————————————————
    Set @SQL = ‘Alter INDEX ‘ + @Indexes + ‘ on ‘ + @TableName + ‘ Rebuild’
    Execute sp_Executesql @sql
    —————————————————————————————————

    fetch next from @acursor_Indexes into @Indexes
    end

    close @acursor_Indexes
    deallocate @acursor_Indexes
    –End Looping

    Truncate Table #TableIndex

    Set @SQL = ‘Select Name, Avg_fragmentation_in_Percent
    FROM sys.dm_db_index_physical_stats (db_id(N”ICDB”),
    Object_id(”’ + @TableName + ”’),null,null,null) as A
    JOIN
    sys.indexes as B on a.Object_ID = b.Object_id
    AND a.index_id = b.index_id
    AND name is not null
    AND name ””’

    Insert INTO #TableIndex(Indexes,Fragment)
    Execute sp_Executesql @sql

    UPDATE A SET Defragment = (SELECT Sum(Fragment) / (Select Count(*) FROM #TableIndex) FROM #TableIndex)
    FROM #Report A
    Where [Table Name] = @TableName

    —————————————————————————————————

    fetch next from @acursor_TableName into @TableName
    end

    close @acursor_TableName
    deallocate @acursor_TableName
    –End Looping

    Update A Set Percentage = (Defragment/Fragment) * 100
    FROM #Report A
    Where Defragment 0
    and Defragment is not null
    and Fragment 0
    and Fragment is not null

    Select *
    FROM #Report
    Where Defragment is not null
    and Defragment ‘0.00’
    and Defragment = Fragment

    Drop table #TableIndex
    Drop Table #Report

    Like

  3. HalfHuman’s two-part suggestion results in the following complete script. I also added a @ViewOnly variable that will allow you to run this script as a test only. I am also wrapping this post with <CODE> </CODE>to keep the webserver from mangling the script.


    -- Specify your Database Name
    USE DatabaseName
    GO

    -- Declare variables
    SET NOCOUNT ON
    DECLARE @tablename VARCHAR(128)
    DECLARE @execstr VARCHAR(255)
    DECLARE @objectid INT
    DECLARE @indexid INT
    DECLARE @frag decimal
    DECLARE @maxfrag decimal
    DECLARE @IdxName varchar(128)
    DECLARE @ViewOnly bit

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

    -- Decide on the maximum fragmentation to allow for.
    SET @maxfrag = 30.0

    -- Declare a cursor.
    DECLARE tables 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'

    -- 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

    -- Loop through all the tables in the database.
    FETCH NEXT
    FROM tables
    INTO @tablename

    WHILE @@FETCH_STATUS = 0
    BEGIN
    -- Do the showcontig of all indexes of the table
    INSERT INTO #fraglist
    EXEC ('DBCC SHOWCONTIG (''' + @tablename + ''')
    WITH FAST, TABLERESULTS, ALL_INDEXES, NO_INFOMSGS')
    FETCH NEXT
    FROM tables
    INTO @tablename
    END

    -- Close and deallocate the cursor.
    CLOSE tables
    DEALLOCATE tables

    -- Declare the cursor for the list of indexes to be defragged.
    DECLARE indexes CURSOR FOR
    SELECT ObjectName, ObjectId, IndexId, LogicalFrag, IndexName
    FROM #fraglist
    WHERE LogicalFrag >= @maxfrag
    AND INDEXPROPERTY (ObjectId, IndexName, 'IndexDepth') > 0

    -- Open the cursor.
    OPEN indexes

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

    WHILE @@FETCH_STATUS = 0
    BEGIN
    IF (@ViewOnly=1)
    BEGIN
    PRINT 'WOULD be executing ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REORGANIZE WITH ( LOB_COMPACTION = ON ) - Fragmentation currently ' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '%'
    END
    ELSE
    BEGIN
    PRINT 'Now executing ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REORGANIZE WITH ( LOB_COMPACTION = ON ) - Fragmentation currently ' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '%'
    SELECT @execstr = 'ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REORGANIZE WITH ( LOB_COMPACTION = ON )'
    EXEC (@execstr)
    END

    FETCH NEXT
    FROM indexes
    INTO @tablename, @objectid, @indexid, @frag, @IdxName
    END

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

    -- Delete the temporary table.
    DROP TABLE #fraglist
    GO

    Like

  4. This is a different version than the prior post – it provides the ability to reorg or rebuild indexes, based on fragmentation value.

    /*
    Added a @ViewOnly variable that will allow you to run this script as a test only and review proposed actions.
    I also enhanced to perform either a reorg or rebuild, based on range of fragmentation value.
    ~Paul DeBrino : infinityrd.com

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

    --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

    */
    -- Specify your Database Name
    USE YourDBname
    GO

    -- Declare variables
    SET NOCOUNT ON
    DECLARE @tablename VARCHAR(128)
    DECLARE @execstr VARCHAR(255)
    DECLARE @objectid INT
    DECLARE @indexid INT
    DECLARE @frag decimal
    DECLARE @maxreorg decimal
    DECLARE @maxrebuild decimal
    DECLARE @IdxName varchar(128)
    DECLARE @ViewOnly bit
    DECLARE @ReorgOptions varchar(255)
    DECLARE @RebuildOptions varchar(255)

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

    -- 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
    SET @ReorgOptions = 'LOB_COMPACTION=ON'
    -- Decide on the maximum fragmentation to allow for a rebuild.
    SET @maxrebuild = 30.0
    -- NOTE: only specifiy FILLFACTOR=x if x is something other than zero:
    SET @RebuildOptions = 'PAD_INDEX=OFF, SORT_IN_TEMPDB=ON, STATISTICS_NORECOMPUTE=OFF, ALLOW_ROW_LOCKS=ON, ALLOW_PAGE_LOCKS=ON'

    -- Declare a cursor.
    DECLARE tables 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'

    -- 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

    -- Loop through all the tables in the database.
    FETCH NEXT
    FROM tables
    INTO @tablename

    WHILE @@FETCH_STATUS = 0
    BEGIN
    -- Do the showcontig of all indexes of the table
    INSERT INTO #fraglist
    EXEC ('DBCC SHOWCONTIG (''' + @tablename + ''')
    WITH FAST, TABLERESULTS, ALL_INDEXES, NO_INFOMSGS')
    FETCH NEXT
    FROM tables
    INTO @tablename
    END

    -- Close and deallocate the cursor.
    CLOSE tables
    DEALLOCATE tables

    -- Declare the cursor for the list of indexes to be defragged.
    DECLARE indexes CURSOR FOR
    SELECT ObjectName, ObjectId, IndexId, LogicalFrag, IndexName
    FROM #fraglist
    WHERE ((LogicalFrag >= @maxreorg) OR (LogicalFrag >= @maxrebuild))
    AND INDEXPROPERTY (ObjectId, IndexName, 'IndexDepth') > 0

    -- Open the cursor.
    OPEN indexes

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

    WHILE @@FETCH_STATUS = 0
    BEGIN
    IF (@frag >= @maxrebuild)
    BEGIN
    IF (@ViewOnly=1)
    BEGIN
    PRINT 'WOULD be executing ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REBUILD WITH ( ' + @RebuildOptions + ' ) -- Fragmentation currently ' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '%'
    END
    ELSE
    BEGIN
    PRINT 'Now executing ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REBUILD WITH ( ' + @RebuildOptions + ' ) -- Fragmentation currently ' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '%'
    SELECT @execstr = 'ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REBUILD WITH ( ' + @RebuildOptions + ' )'
    EXEC (@execstr)
    END
    END
    ELSE IF (@frag >= @maxreorg)
    BEGIN
    IF (@ViewOnly=1)
    BEGIN
    PRINT 'WOULD be executing ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REORGANIZE WITH ( ' + @ReorgOptions + ' ) -- Fragmentation currently ' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '%'
    END
    ELSE
    BEGIN
    PRINT 'Now executing ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REORGANIZE WITH ( ' + @ReorgOptions + ' ) -- Fragmentation currently ' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '%'
    SELECT @execstr = 'ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REORGANIZE WITH ( ' + @ReorgOptions + ' )'
    EXEC (@execstr)
    END
    END

    FETCH NEXT
    FROM indexes
    INTO @tablename, @objectid, @indexid, @frag, @IdxName
    END

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

    -- Delete the temporary table.
    DROP TABLE #fraglist
    GO

    Like

    • Hi Paul,

      I found your script to be very thorough and complete. However, when I run it, I receive the following error:

      Msg 5236, Level 15, State 1, Line 1
      Unable to process object ‘dbo’ because it is a four-part name, which is not supported by any DBCC command.

      Do you know how to fix this problem? Please let me know.

      Thanks,

      Paul

      Like

  5. Hello Pinal Dave,

    Thank for the great topic.

    A few requests –
    1. Please reformat the scripts in this post to show indentation and colorization, as in your original script, and correct the single- and double-quotes to be accepted by SQL when pasting the scripts into SMS or QA.

    2. Please explain why, after running the script with SET @ViewOnly=0 the indexes did not appear to defragment (running the script afterward with SET @ViewOnly=1 showed the same exact frag level).

    Thanks!
    WCM

    Like

  6. Hi Pinal
    I have a SQL 2005 database and I try to do a smart reindex. It works OK fro most of indexes . Database has around 500 indexes and aprox 150 needs to be REBUILT OR REORGANIZED.
    The problem is: no matter how I do rebuilt them …they are still fragmented. Even if I drop index manualy and recreate it after….it comes fragmented. The indexes with problem are clustered and non clustered indexex create on one single column for each table (I mean no composite key) and column type is uniqueidentifier.
    What is really strange..if I rebuild index with fill factor 10….it gets defragmented from 50 to 20 percent. If I use fillfactor 90…it doesn’ get better at all.
    I want to mention that system operation files are ok, I mean no need to defrag HDD.
    Thank you in advance for your help
    Liviu

    Like

  7. Liviu Cornea

    Visit this link,
    Link: http://www.sqlservercentral.com/Forums/Topic438525-145-1.aspx

    Highlights from above link:

    1. Check how many rows are present in the table. ( If very few rows, then you can ignore defraging your index)
    2. Check how many pages are under Index. ( If low then ignore defraging index)
    3. Check fragmentation level of Hard Disk.
    4. Which index you are trying to rebuild/re-organize , Clustered or Non-Clustered ? ( If Non-Clustered, then ignore defraging index)
    5. Instead of using Alter Index ALL …… use , Alter Index Index_name on table rebuild with fillfactor …… ( Personally, I don’t think this will make a difference)

    Quoted directly from forum
    Microsoft has as whitepaper on this.

    “Generally, you should not be concerned with fragmentation levels of indexes with less than 1,000 pages. In the tests, indexes containing more than 10,000 pages realized performance gains, with the biggest gains on indexes with significantly more pages (greater than 50,000 pages).”

    Link: http://www.microsoft.com/technet/prodtechnol/sql/2000/maintain/ss2kidbp.mspx

    Regards,
    IM.

    Like

  8. Hi Dave,

    After running your script in SQL 2005 I still don’t see any index defrag changes not with a 3-rd party monitoring tool or by running this:
    USE master
    SELECT *
    FROM sys.dm_db_index_physical_stats (NULL, NULL, NULL, NULL, NULL)
    order by avg_fragmentation_in_percent

    Pls. advise what can I do other than rebuilding all indexes.

    Your site is SUPER!

    Thank you,
    Alex

    Like

  9. Hello Alex,

    The script is only to defrag index that are fragmented more than 30%. Defragmentation may not reflect in much small tables. So in the output of sys.dm_db_index_physical_stats make sure that:
    1. the object that have high fragmentation percentage is not a heap.
    2. Fragmentation percentage is more than 30
    3. the page_count is not a very small number.

    Regards,
    Pinal Dave

    Like

  10. Hi Pinal,
    I really appreciate you for your response

    I am trying to execute the same code on database where i have many indexes fragmented upto 99%

    this code only defragments few indexes

    what about the other indexes whose fragmention is still 80% after running this code
    please help

    SET NOCOUNT ON;
    DECLARE @tablename VARCHAR(128);
    DECLARE @execstr VARCHAR(255);
    DECLARE @objectid INT;
    DECLARE @indexid INT;
    DECLARE @frag decimal;
    DECLARE @maxfrag decimal;
    — Decide on the maximum fragmentation to allow for.
    SELECT @maxfrag = 30.0;
    — Declare a cursor.
    DECLARE tables 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';
    — Create the table.
    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;
    — Loop through all the tables in the database.
    FETCH NEXT
    FROM tables
    INTO @tablename;
    WHILE @@FETCH_STATUS = 0
    BEGIN;
    — Do the showcontig of all indexes of the table
    INSERT INTO #fraglist
    EXEC (‘DBCC SHOWCONTIG (”’ + @tablename + ”’)
    WITH FAST, TABLERESULTS, ALL_INDEXES, NO_INFOMSGS’);
    FETCH NEXT
    FROM tables
    INTO @tablename;
    END;
    — Close and deallocate the cursor.
    CLOSE tables;
    DEALLOCATE tables;
    — Declare the cursor for the list of indexes to be defragged.
    DECLARE indexes CURSOR FOR
    SELECT ObjectName, ObjectId, IndexId, LogicalFrag
    FROM #fraglist
    WHERE LogicalFrag >= @maxfrag
    AND INDEXPROPERTY (ObjectId, IndexName, ‘IndexDepth’) > 0;
    — Open the cursor.
    OPEN indexes;
    — Loop through the indexes.
    FETCH NEXT
    FROM indexes
    INTO @tablename, @objectid, @indexid, @frag;
    WHILE @@FETCH_STATUS = 0
    BEGIN;
    PRINT ‘Executing DBCC INDEXDEFRAG (0, ‘ + RTRIM(@tablename) + ‘,
    ‘ + RTRIM(@indexid) + ‘) – fragmentation currently ‘
    + RTRIM(CONVERT(VARCHAR(15),@frag)) + ‘%';
    SELECT @execstr = ‘DBCC INDEXDEFRAG (0, ‘ + RTRIM(@objectid) + ‘,
    ‘ + RTRIM(@indexid) + ‘)';
    EXEC (@execstr);
    FETCH NEXT
    FROM indexes
    INTO @tablename, @objectid, @indexid, @frag;
    END;
    — Close and deallocate the cursor.
    CLOSE indexes;
    DEALLOCATE indexes;
    — Delete the temporary table.
    DROP TABLE #fraglist;
    GO

    Like

  11. Hello. Thanks for the fine information provided above. I ran one of the jobs which highlighted fragmentation and stated an intent to resolve it. However, on re-running the job, it showed the same level of fragmentation…. suspecting I had run the job inappropriately, I went to one of the offending indexes using the “object explorer” and have done a “rebuild” a “reorganize” and through “properties”->”fragmentation” have done a “reorganize index”.
    All of these appear to run ok but leave the index with 80% fragmentation…..
    I welcome any suggestions as to why this might be?

    I am running ;
    Microsoft SQL Server Management Studio Express 9.00.3033.00
    Microsoft Data Access Components (MDAC) 2000.086.3959.00 (srv03_sp2_rtm.070216-1710)
    Microsoft MSXML 2.6 3.0 4.0 6.0
    Microsoft Internet Explorer 6.0.3790.3959
    Microsoft .NET Framework 2.0.50727.1433
    Operating System 5.2.3790

    Thanks in advance.
    Regards, Derek

    Like

  12. Don, most probably that happens because the above script(s) use temporary tables. Try to use instead a table variable or a permanent table. I prefer the second solution because it allows me to store also statistical information about defragmentation (e.g. time, previous/post defragmentation values).

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s