I have about 300 tables with 5.5kk rows each. One of the rows is using nvarchar(128) as a datatype (SQL Server 2012).
We decided to change this to int and add FK to dictionary table with all nvarchars.

After doing all of it I removed the nvarchar column, but the size of the tables remained the same. I used DBCC CLEANTABLE and rebuilt the indexes to reclaim free space, but size of the table is still not changing.

So far, the only way I found is to copy all data to the new table.

Question: What am I missing here ? Why the space is still marked as used and I can't free is by shrinking or CREANTABLE command?

Thank you!

Answ: Looks like the answer was pretty simple and I was not able to find it due to my lack of knowledge. The main problem here was heap fragmentation. This query worked for me:

ALTER TABLE [TABLE_NAME] REBUILD 

I am not sure that it's the best way, but at least it's a working one.

Edited1: Sorry, I think I forgot to mention - I had clustered index on the Text field, so I had to remove the index to be able to actually remove the field. Now I have no indexes.

Edited2:

old table:

CREATE TABLE [dbo].[Data_]( [ID] [bigint] PRIMARY KEY IDENTITY(1,1) NOT NULL, [Text] [nvarchar](128) NOT NULL, [Category] [tinyint] NOT NULL, [Country] [nvarchar](2) NOT NULL, [ImportTimestamp] [date] NOT NULL ) 

new table:

CREATE TABLE [dbo].[Data_New]( [ID] [int] PRIMARY KEY IDENTITY(1,1) NOT NULL, [Category] [tinyint] NOT NULL, [Country] [nvarchar](2) NOT NULL, [TextID] [int] NOT NULL) ALTER TABLE [dbo].[Data_New] WITH CHECK ADD FOREIGN KEY([TextID]) REFERENCES [dbo].[Dictionary] ([Id]) 

Copy script:

INSERT INTO Data_New ([Category] ,[Country] ,[TextID]) SELECT [Category] ,[Country] ,[TextID] FROM Data_ 

2 Answers

Are you sure the table should get smaller because of this column change? nvarchar(128) doesn't mean SQL Server will allocate 128 bytes (+2) for saving data, for example the string 'test'. The string 'test' will only take 6 bytes. Maybe you need to check free space in table first:

SELECT t.NAME AS TableName, s.Name AS SchemaName, p.rows AS RowCounts, SUM(a.total_pages) * 8 AS TotalSpaceKB, SUM(a.used_pages) * 8 AS UsedSpaceKB, (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB FROM sys.tables t INNER JOIN sys.indexes i ON t.OBJECT_ID = i.object_id INNER JOIN sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id INNER JOIN sys.allocation_units a ON p.partition_id = a.container_id LEFT OUTER JOIN sys.schemas s ON t.schema_id = s.schema_id WHERE t.NAME = 'TABLE_NAME' AND t.is_ms_shipped = 0' AND i.OBJECT_ID > 255 GROUP BY t.Name, s.Name, p.Rows ORDER BY t.Name 
6

If you don't care about understanding the issue and just want to get rid of it then rebuilding the index is a sure way to remove any waste. This is because a index rebuild constructs the physical data structures freshly. Whatever the old index contained will not matter anymore.

There are trade-offs involved when comparing this with CLEANTABLE. If a rebuild does the job for you I'd always do that because it is such a complete solution.

When I say "index" in this answer I mean all physical structures that contain one of the columns you care about. That can be b-tree indexes or the heap the table is based on.

5

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.