Check Whether Update Statistics Required

Applicability:

SQL Server 2000: Tested

SQL Server 2005: Tested

SQL Server 2008: Tested

SQL Server 2008R2: Tested

SQL Server 2012: Not Tested

Credits:

Author: ChillyDBA

Date: 28 Aug 2008

Description

Many times I have been involved in writing scheduled batch tasks that manipulate large data volumes. Common types of tasks are archiving and migration, but ETL tasks can also deal with large data volumes.

Often the source/destination for the data is an existing table that is subject to ongoing changes. This can cause the performance of the batch to vary wildly, especially in data archiving tasks where deletions can invalidate index usage due to the volume of changes.

During a recent data migration project, rather than blindly issue an update statistics command for each iteration, I wrote this small function to allow the update statistics command to be invoked only when necessary.

The threshold for change is set at 10% as I found this to work for the task at the time, but it can easily be modified.

Code

Function:

DROP FUNCTION udf_IsIndexStatistsUpdateRequired

GO

CREATE FUNCTION udf_IsIndexStatistsUpdateRequired (@TableName VARCHAR(256)) RETURNS BIT

AS

/****************************************************

Purpose: To determine whether an update stats is required on

a table depending on the percentage of changed rows

Threshold currently set at 10%

Author: ChillyDBA

History: 28 Aug 08 - Initial Issue

****************************************************/

BEGIN

DECLARE @RebuildNeeded BIT,

@PercentRowsChanged DECIMAL(18,2),

@Threshold DECIMAL(18,2)

SELECT @Threshold = 10.0

SELECT @PercentRowsChanged = (CAST(MAX(RowModCtr) AS DECIMAL(18,2))/ CAST(MAX(RowCnt) AS DECIMAL(18,2))) * 100

FROM dbo.SysIndexes

WHERE ID = OBJECT_ID(@TableName)

AND IndID > 0

AND Name NOT LIKE '_WA_Sys%'

IF @PercentRowsChanged > @Threshold

SELECT @RebuildNeeded =1

ELSE

SELECT @RebuildNeeded = 0

RETURN @RebuildNeeded

END

Test Code:

--create and index to measure against

CREATE NONCLUSTERED INDEX [IX_MaritalStatus] ON [dbo].[ProspectiveBuyer]

(

[MaritalStatus] ASC

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO

-- check before - there should be no need to update the statistics

SELECT dbo.udf_IsIndexStatistsUpdateRequired ('dbo.ProspectiveBuyer')

-- perform some updates (~50% of the rows changed)

UPDATE ProspectiveBuyer

SET MaritalStatus = 'P'

WHERE MaritalStatus = 'M'

UPDATE ProspectiveBuyer

SET MaritalStatus = 'M'

WHERE MaritalStatus = 'P'

-- check after - statistics should require updating

SELECT dbo.udf_IsIndexStatistsUpdateRequired ('dbo.ProspectiveBuyer')

-- remove the test index

DROP INDEX ProspectiveBuyer.IX_MaritalStatus

GO