Earlier, I had written two articles related to Shrinking Database. I wrote about why Shrinking Database is not good.
- SQL SERVER – SHRINKDATABASE For Every Database in the SQL Server
- SQL SERVER – What the Business Says Is Not What the Business Wants
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.
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
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
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
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)
54 Comments. Leave new
Nice .. thanks pinal
Thx Pinal, I was searching for this type of article.
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
Are there any restorative procedures for dbs that have been shrunk regularly?
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.
@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]
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
Thank you for the post. Greatly appreciate and enjoyed it
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
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 .
Hi Pinal
Its a very good and really very helpful post for me.
Vandana
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.
Not sure but tested on denali…After truncating I see 0 fragmentation.
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.
great example!! thanks.
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
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
OK shrinking is not good, but what is the solution, what to do?
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?
Hi Pinal,
Nice Article with awsome Examples