Sql-server – Deleting data from a table containing LOBs did not reduce the amount of data reported INTERNALLY by the table or database

sql serversql-server-2012standard-edition

Running SQL Server 2012 standard, using both replication data and log shipping. Transaction log backups are created every few minutes.

This question is NOT about the amount of space reported by the file system.

We have a log table with four VARCHAR(max) columns. The table currently holds roughly 180 GB of data. The table grows at a rate of roughly 10 GB per month.

In an attempt to free up space for future growth, I recently deleted (in chunks over a 36 hour period) close to 30% of the data.

Based on the size of the table and the number of rows being removed, I expected the size of the table to be reduced by roughly 55-60 GB. I verified this by taking a sample and calculating the average row size, and then multiplying that by the number of rows to be removed. However, after finishing the delete, the table size has not changed.

While I am not looking to shrink the size of the .mdf file, I did check to see if the database had any free space that could be shrunk, but there is 0% free space.

So – I am confused. I've verified the data that I deleted from the table is no longer there. I even compared it against a backup to get a before & after. The number of rows in the database has changed, but the data space for the table has not decreased.

My questions then are:

1) Why did deleting data from the table not reduce the size of the table?
2) What can I do to actually reduce the size of the table?

EDIT:

  • I am determining the space used by the table by calling sp_spaceused 'tablename'.
  • I have used dbcc updateusage (0, tablename) prior to calling sp_spaceused.
  • I am NOT trying to free up disk space – although an analysis indicates that there is 0% space available to be reclaimed.
  • I have run an index reorganize with ( LOB_COMPACTION = ON ). Fragmentation is below 1%.
  • The table is not a heap. Table definition below.
 CREATE TABLE [dbo].[LARGE_LOG_TABLE](
  [ID] [varchar](36) NOT NULL,
  [DOC_ID] [varchar](36) NOT NULL,
  [REQUEST_DATA] [varchar](max) NULL,
  [REQUEST_DATA_XSLT] [varchar](max) NULL,
  [RESPONSE_DATA] [varchar](max) NULL,
  [RESPONSE_DATA_XSLT] [varchar](max) NULL,
  [ACTIVE_FLAG] [varchar](1) NULL,
  [CREATED_BY] [varchar](100) NULL,
  [DATE_CREATED] [datetime] NULL CONSTRAINT [DF_LARGE_LOG_TABLE_CREATE_DT]  DEFAULT (getdate()),
  [USER_LAST_UPDATED] [varchar](100) NULL,
  [DATE_LAST_UPDATED] [datetime] NULL CONSTRAINT [DF_LARGE_LOG_TABLE_LAST_DT]  DEFAULT (getdate()),
 CONSTRAINT [PK_LARGE_LOG_TABLE] PRIMARY KEY CLUSTERED 
 ( [ID] ASC ) ON [PRIMARY] ) 
 ON [PRIMARY] 
 TEXTIMAGE_ON [PRIMARY]

EDIT 2:

SELECT * FROM sys.dm_db_index_physical_stats (db_id(), OBJECT_ID('database..LARGE_LOG_TABLE'), default, default, default)

sys.dm_db_index_physical_stats

EDIT 3:

The answers provided to the question at Delete query does not appear to open space on the SQL Server refer to freeing up space on disk / shrinking the physical size of the database. This question refers to removing rows containing LOB data and yet the database (internally) shows the LOB data has not been removed. We removed 600000 rows of data, but the space used on the table level (not the file system level) was only reduced by a few bytes – not the expected 60 GB. This question is about removing the LOB data.

Best Answer

@edze asked a great question. I've known that LOBs are stored separately from the table data (with a pointer referencing the storage location), but I didn't know specifically where they were stored.

While searching for more information on where LOB data is stored, I found the following links:

http://itknowledgeexchange.techtarget.com/sql-server/deleting-lob-data-and-shrinking-the-database/

The key take away here (focused on a database shrink) is the following:

Do the database deletion as normal. 
Then backup and restore the database. 
Then do the shrink, followed by rebuilding the clustered indexes in order to fix the fragmentation issue which the shrink will introduce.

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/f80ad8c7-e6c9-4538-a89b-8d6727050b9c/release-space-used-after-sql-server-delete-in-rows-with-blob-varbinarymax-using-textimage-on?forum=sqldatabaseengine

Here the key is:

  1. Rebuild the clustered indexes of the table.
  2. Execute DBCC UPDATEUSAGE (0)

With this being Standard edition, a rebuild will require a LONG lock - but it looks like the only way to recover the space.