views:

2753

answers:

5

I have a table on an MS SQL 2005 server that was about 4gb in size.

(about 17 million records)

I changed one of the fields from datatype char(30) to char(60). (there are in total 25 fields most of which are char(10) so the amount of char space adds up to about 300)

This caused the table to double in size (over 9gb)

I then changed the char(60) to varchar(60) and then ran a function to cut extra whitespace out of the data (so as to reduce the average length of the data in the field to about 15)

This did not reduce the table size. Shrinking the database did not help either.

Short of actually recreating the table structure and copying the data over (that's 17 million records!) is there a less drastic way of getting the size back down again?

A: 

I had a similar problem here http://stackoverflow.com/questions/487324/sql-server-converting-ntext-to-nvarcharmax that was related to changing ntext to nvarchar(max).

I had to do an UPDATE MyTable SET MyValue = MyValue in order to get it to resize everything nicely.

This obviously takes quite a long time with a lot of records. There were a number of suggestions as how better to do it. They key one was a temporary flag indicated if it had been done or not and then updating a few thousand at a time in a loop until it was all done. This meant I had "some" control over how much it was doing.

On another note though, if you really want to shrink the database as much as possible, it can help if you turn the recovery model down to simple, shrink the transaction logs, reorganise all the data in the pages, then set it back to full recovery model. Be careful though, shrinking of databases is generally not advisable, and if you reduce the recovery model of a live database you are asking for something to go wrong.

Robin Day
A: 
JRL
+1  A: 

Well it's clear you're not getting any space back ! :-)

When you changed your text fields to CHAR(60), they are all filled up to capacity with spaces. So ALL your fields are now really 60 characters long.

Changing that back to VARCHAR(60) won't help - the fields are still all 60 chars long....

What you really need to do is run a TRIM function over all your fields to reduce them back to their trimmed length, and then do a database shrinking.

After you've done that, you need to REBUILD your clustered index in order to reclaim some of that wasted space. The clustered index is really where your data lives - you can rebuild it like this:

ALTER INDEX IndexName ON YourTable REBUILD

By default, your primary key is your clustered index (unless you've specified otherwise).

Marc

marc_s
I did that... "ran a function to cut extra whitespace out of the data"
MrVimes
specifically I did update table [table] set [column] = replace([column],' ','') where year = 2009 (then again for 2008, 2007, and so on)
MrVimes
Have you tried UPDATE Table SET Column = RTRIM(Column) across all years? Still no change?
marc_s
All years at once? Or all years one year at a time? I'm currently trying it one year at a time
MrVimes
Right. I did that. It made no difference,
MrVimes
what if I do "Empty file by migrating the data to other files in the same filegroup"
MrVimes
You need to rebuild your clustered index to reclaim (at least some of) the wasted space. Worked for me.
marc_s
SUCCESS!! That worked. I'll tick yours as the answer that worked. I really appreciate this not only because you've helped me fix it, but you've also increased my knowledge.
MrVimes
A: 

Alternatively, you could do a full table rebuild to ensure there's no extra data hanging around anywhere:

CREATE TABLE tmp_table(<column definitions>);
GO
INSERT INTO tmp_table(<columns>) SELECT <columns> FROM <table>;
GO
DROP TABLE <table>;
GO
EXEC sp_rename N'tmp_table', N'<table>';
GO

Of course, things get more complicated with identity, indexes, etc etc...

thecoop
This might be a little easier:SELECT * INTO tmp_table FROM my_tableGODROP TABLE my_tableGOEXEC sp_rename N'tmp_table', N'my_table';GO
beach
+2  A: 

You have not cleaned or compacted any data, even with a "shrink database".

DBCC CLEANTABLE

Reclaims space from dropped variable-length columns in tables or indexed views.

However, a simple index rebuild if there is a clustered index should also do it

ALTER INDEX ALL ON dbo.Mytable REBUILD

A worked example from Tony Rogerson

gbn
Thanks also to you for providing the answer. though I saw the other guy's first. Sorry. :)
MrVimes
Thanks. Although I added my answer within minutes of his revision: his first answer did not mention index rebuild
gbn