SQL SERVER – Shrinking Database is Bad – Increases Fragmentation – Reduces Performance

Earlier, I had written two articles related to Shrinking Database. I wrote about why Shrinking Database is not good.

I received many comments on Why Database Shrinking is bad. Today we will go over a very interesting example that I have created for the same. Here are the quick steps of the example.

  • Create a test database
  • Create two tables and populate with data
  • Check the size of both the tables
    • Size of database is very low
  • Check the Fragmentation of one table
    • Fragmentation will be very low
  • Truncate another table
  • Check the size of the table
  • Check the fragmentation of the one table
    • Fragmentation will be very low
  • SHRINK Database
  • Check the size of the table
  • Check the fragmentation of the one table
    • Fragmentation will be very HIGH
  • REBUILD index on one table
  • Check the size of the table
    • Size of database is very HIGH
  • Check the fragmentation of the one table
    • Fragmentation will be very low

Here is the script for the same.

USE MASTER
GO
CREATE DATABASE ShrinkIsBed
GO
USE ShrinkIsBed
GO
-- Name of the Database and Size
SELECT name, (size*8) Size_KB
FROM sys.database_files
GO
-- Create FirstTable
CREATE TABLE FirstTable (ID INT,
FirstName VARCHAR(100),
LastName VARCHAR(100),
City VARCHAR(100))
GO
-- Create Clustered Index on ID
CREATE CLUSTERED INDEX [IX_FirstTable_ID] ON FirstTable
(
[ID] ASC
) ON [PRIMARY]
GO
-- Create SecondTable
CREATE TABLE SecondTable (ID INT,
FirstName VARCHAR(100),
LastName VARCHAR(100),
City VARCHAR(100))
GO
-- Create Clustered Index on ID
CREATE CLUSTERED INDEX [IX_SecondTable_ID] ON SecondTable
(
[ID] ASC
) ON [PRIMARY]
GO
-- Insert One Hundred Thousand Records
INSERT INTO FirstTable (ID,FirstName,LastName,City)
SELECT TOP 100000 ROW_NUMBER() OVER (ORDER BY a.name) RowID,
'Bob',
CASE WHEN ROW_NUMBER() OVER (ORDER BY a.name)%2 = 1 THEN 'Smith'
ELSE 'Brown' END,
CASE WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 1 THEN 'New York'
WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 5 THEN 'San Marino'
WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 3 THEN 'Los Angeles'
ELSE 'Houston' END
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
GO
-- Name of the Database and Size
SELECT name, (size*8) Size_KB
FROM sys.database_files
GO
-- Insert One Hundred Thousand Records
INSERT INTO SecondTable (ID,FirstName,LastName,City)
SELECT TOP 100000 ROW_NUMBER() OVER (ORDER BY a.name) RowID,
'Bob',
CASE WHEN ROW_NUMBER() OVER (ORDER BY a.name)%2 = 1 THEN 'Smith'
ELSE 'Brown' END,
CASE WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 1 THEN 'New York'
WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 5 THEN 'San Marino'
WHEN ROW_NUMBER() OVER (ORDER BY a.name)%10 = 3 THEN 'Los Angeles'
ELSE 'Houston' END
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
GO
-- Name of the Database and Size
SELECT name, (size*8) Size_KB
FROM sys.database_files
GO
-- Check Fragmentations in the database
SELECT avg_fragmentation_in_percent, fragment_count
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID('SecondTable'), NULL, NULL, 'LIMITED')
GO

Let us check the table size and fragmentation.

SQL SERVER - Shrinking Database is Bad - Increases Fragmentation - Reduces Performance ShrinkFrag1

Now let us TRUNCATE the table and check the size and Fragmentation.

-- TRUNCATE and SHRINKDB
TRUNCATE TABLE SecondTable
GO
-- Name of the Database and Size
SELECT name, (size*8) Size_KB
FROM sys.database_files
GO
-- Shrink the Database
DBCC SHRINKDATABASE (ShrinkIsBed);
GO
-- Name of the Database and Size
SELECT name, (size*8) Size_KB
FROM sys.database_files
GO
-- Check Fragmentations in the database
SELECT avg_fragmentation_in_percent, fragment_count
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID('SecondTable'), NULL, NULL, 'LIMITED')
GO

SQL SERVER - Shrinking Database is Bad - Increases Fragmentation - Reduces Performance ShrinkFrag2

You can clearly see that after TRUNCATE, the size of the database is not reduced and it is still the same as before TRUNCATE operation. After the Shrinking database operation, we were able to reduce the size of the database. If you notice the fragmentation, it is considerably higher.

The major problem with the Shrink operation is that it increases fragmentation of the database to very high value. Higher fragmentation reduces the performance of the database as reading from that particular table becomes very expensive.

One of the ways to reduce the fragmentation is to rebuild index on the database. Let us rebuild the index and observe fragmentation and database size.

-- Rebuild Index on FirstTable
ALTER INDEX IX_SecondTable_ID ON SecondTable REBUILD
GO
-- Name of the Database and Size
SELECT name, (size*8) Size_KB
FROM sys.database_files
GO
-- Check Fragmentations in the database
SELECT avg_fragmentation_in_percent, fragment_count
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID('SecondTable'), NULL, NULL, 'LIMITED')
GO

SQL SERVER - Shrinking Database is Bad - Increases Fragmentation - Reduces Performance ShrinkFrag3

You can notice that after rebuilding, Fragmentation is reduced to a very low value (almost same to the original value); however the database size increases way higher than the original. Before rebuilding, the size of the database was 5 MB, and after rebuilding, it is around 20 MB. Regular rebuilding the index is rebuilt in the same user database where the index is placed. This usually increases the size of the database.

Look at irony of the Shrinking database. One person shrinks the database to gain space (thinking it will help performance), which leads to increase in fragmentation (reducing performance). To reduce the fragmentation, one rebuilds index, which leads to the size of the database to increase way more than the original size of the database (before shrinking). Well, by Shrinking, one did not gain what he was looking for usually.

Rebuild indexes is not the best suggestion as that will create database grow again.

Let us run following script where we Shrink the database and REORGANIZE.

-- Name of the Database and Size
SELECT name, (size*8) Size_KB
FROM sys.database_files
GO
-- Check Fragmentations in the database
SELECT avg_fragmentation_in_percent, fragment_count
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID('SecondTable'), NULL, NULL, 'LIMITED')
GO
-- Shrink the Database
DBCC SHRINKDATABASE (ShrinkIsBed);
GO
-- Name of the Database and Size
SELECT name, (size*8) Size_KB
FROM sys.database_files
GO
-- Check Fragmentations in the database
SELECT avg_fragmentation_in_percent, fragment_count
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID('SecondTable'), NULL, NULL, 'LIMITED')
GO
-- Rebuild Index on FirstTable
ALTER INDEX IX_SecondTable_ID ON SecondTable REORGANIZE
GO
-- Name of the Database and Size
SELECT name, (size*8) Size_KB
FROM sys.database_files
GO
-- Check Fragmentations in the database
SELECT avg_fragmentation_in_percent, fragment_count
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID('SecondTable'), NULL, NULL, 'LIMITED')
GO

SQL SERVER - Shrinking Database is Bad - Increases Fragmentation - Reduces Performance ShrinkFrag4

You can see that REORGANIZE does not increase the size of the database or remove the fragmentation.

Again, I no way suggest that REORGANIZE is the solution over here. This is purely observation using the demo. Read the blog post of Paul Randal.

Following script will clean up the database

-- Clean up
USE MASTER
GO
ALTER DATABASE ShrinkIsBed
SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
DROP DATABASE ShrinkIsBed
GO

There are few valid cases of the Shrinking database as well, but that is not covered in this blog post. We will cover that area some other time in the future. Additionally, one can rebuild index in the tempdb as well, and we will also talk about the same in future.

Are you Shrinking your database? Well, when are you going to stop Shrinking it?

Reference: Pinal Dave (https://blog.sqlauthority.com)

SQL Index, SQL Scripts, SQL Server, SQL Server DBCC
Previous Post
SQL SERVER – Quick Look at SQL Server Configuration for Performance Indications
Next Post
SQL SERVER – What is Fill Factor and What is the Best Value for Fill Factor

Related Posts

54 Comments. Leave new

  • Nice .. thanks pinal

    Reply
  • Thx Pinal, I was searching for this type of article.

    Reply
  • Andrew Mogford
    January 19, 2011 1:53 pm

    Pinal,

    Good to see this article discouraging the use of Shrink Database. Most of the time when database files get too large it is the transaction logs that have grown, rather than the main database. In these cases the backup regime is not set-up properly or has failed. Some free space in the main database files is a good thing, because it makes future inserts faster.

    I have only ever used shrink database very rarely on a production database. However, if very large amounts of data have been removed from a database, then I think it maybe appropriate.

    Thank you for your excellent blog

    Reply
  • Are there any restorative procedures for dbs that have been shrunk regularly?

    Reply
  • A good detailed explanation on why shrinking is bad. The question i have is since rebuilding indexes increases the database size, what would be a soultion to handle the issue, also REORGANIZE is not recommended either. Could you write a post about this topic: Possible solutions for decreasing fragmentation and not increasing the database size.

    Reply
  • @pin

    You have a solution in the link Pinal linked to https://www.sqlskills.com/blogs/paul/why-you-should-not-shrink-your-data-files/

    [quote]

    So what if you *do* need to run a shrink? For instance, if you’ve deleted a large proportion of a very large database and the database isn’t likely to grow, or you need to empty a file before removing it?

    The method I like to recommend is as follows:

    * Create a new filegroup
    * Move all affected tables and indexes into the new filegroup using the CREATE INDEX … WITH (DROP_EXISTING) ON syntax, to move the tables and remove fragmentation from them at the same time
    * Drop the old filegroup that you were going to shrink anyway (or shrink it way down if its the primary filegroup)
    [/quote]

    Reply
  • Dear Sir,

    Your posted on Shrink Database is Bad is very awesome.

    However, I noticed some redundancy on your database scripts after the words of:

    “Now let us TRUNCATE the table and check the size and Fragmentation.”

    Please help and advise.

    Many thanks for your kind help and sharing.

    Thanks,
    Edwin

    Reply
  • Thank you for the post. Greatly appreciate and enjoyed it

    Reply
  • The sql script for “Now let us TRUNCATE the table and check the size and Fragmentation.” is missing the above post.

    However thanks a lot for the articles.

    Regards,
    Parikshit

    Reply
  • Hi pinal, i tried to shrink the database , but still the size is not reducing. do i have to change any properties for that. before shrinking and after shrinking the size is same .

    Reply
  • Hi Pinal
    Its a very good and really very helpful post for me.

    Vandana

    Reply
  • I had a 5GB database, about 10 tables, one table had about 45 million rows and took about 2GB on disk. Each table has 1 CLUSTERED index with max 2 key fields, most only 1 key.

    If you REBUILD that, it would increase the database size on disk (not actual data) to over 7GB. Fragmentation would be next to 0%. If you SHRINK the file/database, it would reduce the size back down, but would increase fragmentation to about 98%.

    If you REORGANIZE, it would not change the size.and fragmentation would go down to 0.01%.

    It’s hard sometimes to get clients with 300GB databases to just double or triple the disk space, but it’s what should happen. (there should always be plenty of space to grow).

    What I need to create now is a test to illustrate to the client how their app performs at 98% fragmentation and how it performs at 0% fragmentation. That would be the ‘money query’.

    OH YEAH!!

    IF YOU DROP THE CLUSTERED INDEXES, you will get your HEAP back. Heaps do not shrink well. If I tried to shrink my table with heaps, instead of clustered indexes, it would shrink very little each time. Once I re-added a Clustered index on the large table, the free space was returned and I was able to then shrink and then reorganize to get back in the game.

    Thanks for this post.

    Reply
  • Not sure but tested on denali…After truncating I see 0 fragmentation.

    Reply
  • Hi. I read your stuff any chance I get. Thanks for all your help in the past. I am trying to deal with a database that got too big because I neglected to routinely clean out tables of data that builds up rapidly and doesn’t need to be kept. Now I want to shrink it. Like emma above, I am getting 0 for my fragmentation after running your scripts, rather than the big number. Is it possible that a patch fixed this issue since you wrote this article? If not, what do you advise I do? I want to gain a better score on my ‘first byte time’ for my website, and one of the suggestions is to make my database more efficient including cleaning out unused data and (I assume) to make it smaller, thus more efficient. If I shouldn’t shrink it, what are my options.

    Reply
  • Kevin Hecker
    July 5, 2012 5:30 pm

    great example!! thanks.

    Reply
  • Running the sys.dm_db_index_physical_stats query after truncate the table.
    I got the result like this avg_fragmentation_in_percent fragment_count
    0 0
    what might cause the problem?I use the sql server 2008 R2

    Thanks in advance

    Reply
  • Running the sys.dm_db_index_physical_stats query after truncate the table.
    I got the result like this avg_fragmentation_in_percent fragment_count
    0 0
    what might cause the problem?I use the sql server 2008 R2

    Thanks in advance

    Reply
  • Halivudestevez
    August 21, 2012 2:07 pm

    OK shrinking is not good, but what is the solution, what to do?

    Reply
  • I have the same problem as Diyig:
    running the sys.dm_db_index_physical_stats query after truncate the table.
    I got the result like this avg_fragmentation_in_percent fragment_count
    0 0
    why?

    Reply
  • Hi Pinal,
    Nice Article with awsome Examples

    Reply

Leave a Reply