Shrink SQL Server data file, but not all at once?

5,422

Solution 1

No, using DBCC SHRINKFILE ('filename', target_size) is the right way to do it.

If you want to do it in "chunks", you can either set progressively smaller target sizes, or just let it run as long as you can before it gets cancelled.

A few comments:

  • Put a reasonable target size, with some margin of allowed free space. Maybe 90GB total for 75GB of data?
  • While the shrink is running, check the activity monitor to see if the SPID is being blocked. If there is an open transaction on a page at the very end of the file, then shrink won't be able to move it until that transaction commits or is rolled back.
  • Is the spid actually making progress? (The CPU and IO numbers are changing)
  • Shrink can sometimes take a very, very long time, but it should save its progress (meaning it moves 1 page at a time, and when it gets cancelled, all the completed page moves have already been done)
  • After cancelling the shrink, try doing a DBCC SHRINKFILE ('filename', TRUNCATEONLY). It should recover all the space its already freed at the end of the file (see my prior point)
  • If you get desperate, try restarting SQL in single-user mode, so you know that nothing else is working against the db at that time (obviously, this could be impossible on a prod server)
  • Once you are able to complete the shrink, make sure to do a full reindex on the database to eliminate the fragmentation that the shrink creates. This may reclaim some of the space you just freed.
  • If you still can't get the shrink to work, check out some of the discussion on this SO question. There are apparently some situations where shrinks might not progress.

Solution 2

We have approached several options in our environment:

  1. If old tables can be faced-out, create new tables on a brand new filegroup and default the database to it. Overtime, drop the old tables until the previous filegroup is empty. Then drop it.
  2. If old tables cannot be faced-out but contain historical data that can be offlined for several hours, create a new empty table pointing to a new filegroup. Swap the tables and start copying over rows from the old table to the new one in batches. This can cause fragmentation though.
  3. Issue a DBCC SHRINKFILE with EMTPYFILE option and the DB will move all the objects to the new file. Then you can drop the old file. This can take a long time though.
  4. Recreate the clustered index (primary key with DROP_EXISTING) of all tables into the new filegroup. This will lock the table though.

Good Luck

Share:
5,422

Related videos on Youtube

SqlRyan
Author by

SqlRyan

My name is Ryan McCauley, and I'm a database/reporting manager for a mid-size cable company. I spent a number of years as a .NET developer (mostly of the VB.NET variety), but now largely focus on T-SQL and the reporting with the Microsoft BI stack. On my own time, I build small apps to help get things done a little better, and have a couple posted at Codeplex: SQL Space Map - if you have some large SQL Server databases and you'd like a visual picture of which tables and indexes are taking up that space, it's the tool for you. SQL Server Contention Monitor - watches as may SQL Servers as you want and alerts you to blocked SPIDs, showing you the block tree (which process is blocking which, an what others are affected). It's still in a pretty alpha-ish phase, but it despite some instability at times, it gets the job done. Check them out and let me know what feedback you have!

Updated on September 17, 2022

Comments

  • SqlRyan
    SqlRyan over 1 year

    I have a database file that's currently 150GB, but only 75GB is being used - it's because I moved all the indexes (the other 75GB) to a new data file. I'd like to reclaim at least part of the space from this data file, but when I attempt to shrink the file, it "Executes" indefinitely, eventually being cancelled because of a network interruption or something else out of my control (after a day of running). Even using the "shrink to specific size" feature and specifying that it just trim off 10MB never seems to return - it just sits until the process is interrupted.

    Is there another way that I can reclaim this space, even a little at a time?

    EDIT: Somebody posted a link explaining why I shouldn't shrink my database. I understand, and I want to shrink it anyways. Disk space is at a premium on this server, and the database will not expand again into this unused space for a very long time - as I stated earlier, I migrated indexes out of the data file to free up this space, so now it's wasted.

    • jl.
      jl. over 13 years
      Realize you want to reclaim your space but here is a link to an article about why not to shrink datafiles: sqlskills.com/BLOGS/PAUL/post/…
    • 0100110010101
      0100110010101 over 13 years
      Is the db FULL or SIMPLE recovery mode? How's your log space? What's the load like on the database while you're doing the shrink?
  • David Mackintosh
    David Mackintosh over 10 years
    "Faced out" -- do you mean "phased out"? As in retired/removed? Or is this a SQL term?