I've read too many horror stories to even touch any T-SQL command with SHRINK in it, but I need to sort out a number of my SQL Server 2008 databases that have bloat.

My databases consist of a simple set of tables, which have primary keys and standard indexes added onto these tables.

With the above in mind, I've created the following:

DECLARE @sourcedb NVARCHAR(MAX) = N'DB100';

SELECT @sql += N'SELECT * INTO ' + @destdb + '.dbo.' + name + 
               ' FROM ' + @sourcedb + '.dbo.' + name + ';' FROM sys.tables;
EXEC sp_executesql @sql;

use DB200

-- Re-add Primary Keys
etc etc

-- Re-Add Indexes



etc etc

My plan was to then backup the new database and restore it over the old one in the twilight hours.

Early test shows that a 365MB database goes down to 147MB and everything seems to be OK.

Can you see any pitfalls in my plan?

There is potential to delete a lot of rows from my databases over time, as the user can remove redundant information in a few clicks, so I'm assuming that the select/insert option is only copying over the rows that are currently in each table and therefore the bloat of days past is left by the wayside.

  • 2
    Before you go that route have you checked your free space in datafiles? What are you trying to gain by shrinking? Did you check your database growth rate? If you have to do it read this article by Andy. am2.co/2016/04/shrink-database-4-easy-steps – SqlWorldWide Aug 2 '17 at 11:41
  • 3
    Most of the horror stories about shrinking the database are from people who do it all the time. If it's a one-off type thing then it's fine, just follow the appropriate steps. Check out this answer I gave to a similar question. dba.stackexchange.com/questions/173084/… – Jonathan Fite Aug 2 '17 at 11:53

it's not a good idea to shrink your database on a regular basis.

It can make lots of sense to shrink your database after a large amount of data has been removed permanently.

There are two main reasons not to shrink your database:

  1. Fragmentation: When your tell SQL Server to shrink a data file, it has to free up space at the end of the file. It does so by moving whatever pages are at the end of the file to ask close to the start of the file as possible. This causes your indexes and tables to get fragmented.

    De-fragmenting your indexes/tables requires a certain amount of free space. And, de-fragmenting can release space as well. So, after defragmenting your DB, you are likely to have the most free space you ever see. Which makes it tempting to shrink the database. Which makes it so you need to de-fragment it. you see the vicious circle here.

  2. Cost of re-growing: The other issue is that, if your database has grown to its current size, then at some point all the space was needed. It may not be needed on a daily basis, but it was needed at some point. You may have a process that requires extra space (like for instance, de-fragmentation, as noted above; or, a nightly data load into a staging table for processing).

    If you shrink your data file smaller than what it needs for normal maintenance, then it will need to regrow. If you have instant file instantiation turned on, this isn't as expensive as it would otherwise be. However, that doesn't help your log file; if you shrink that, it has to zero out the new file space when it has to grow.

    You can be manually juggling disk space in such a way that, if all your files were the size they need to be for a week, you'd be out of disk space. This can be a valid temporary solution (if one with some performance hits). However, if any of this is being done manually? You better never go on vacation, because things will blow up before you're back.

Now, as noted, there are good reason (and good ways) to shrink your database. If you have greatly reduced the amount of data in the database, and it won't be growing back for a long time, then releasing space for other use makes perfectly good sense.

For what you're talking about, it's possible that a shrink and a defragmentation may be faster than copying everything to a new database. It's almost certainly safer; for your plan, you have to make sure that everything in place in the original DB is in place in the new DB. Copying over tables that include foreign keys means you have to be sure you copy the data over in the right order.

A few tips:

  • If you know something major is about to happen (loading millions of rows for a new marketing campaign, for instance), don't bother shrinking now.
  • Never use DBCC SHRINKDATABASE; SQL Server decides which files to shrink, whether data, log, or other. You want to be in control of what space is freed. Use DBCC SHRINKFILE.
  • While you should definitely do a test run before doing this in production, realize that (unless your test DB is a restore of your production DB) how things go in test won't necessarily reflect how things go in production.
  • While I think this is safer than your new plan, it's not a bad idea to take a backup before you start, to be safe.
  • DBCC SHRINKFILE might not block production activity. Then again, it might. It's safest to plan this during a planned outage.
  • Plan on defragmenting your database as a part of that outage, after your shrink finishes. The DB may grow some during this; that's fine, and the extra space should be left in place.
| improve this answer | |
  • Hi RDFozz many thanks for this detailed explanation. Apols for being a newbie but how do I do the defrag tables bit? I'm assuming alter index... rebuild is the solution to defrag the indexes?? – Tom Brown Aug 3 '17 at 9:22
  • Yes, that would work. You can also set up a maintenance plan to do this. If you don't want to make it a regularly scheduled process, create it with a schedule, but disable the schedule (it'll show up in your SQL Server Agent's Job Activity Monitor, in SSMS), and run it manually as needed. I personally recommend Ola Hallengren's SQL Maintenance scripts - IndexOptimize is the one you'd be interested in. However, as a one-off, the maintenance plan is probably adequate. – RDFozz Aug 3 '17 at 14:39

So my hunch for the main reason why space usage is going down is due to index/table fragmentation. Query sys.dm_db_index_physical_stats on the old database then compare to same query on new database. Check col avg_page_space_used_in_percent (use mode=detailed)

Instead, perhaps use ALTER INDEX ... REBUILD/REORGANIZE regularly to maintain indexes in place. This approach should also be faster than if you copied and recreated the indexes. You're freeing space after future deletes as well.

Hope that's helpful.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.