Script:



/****************The Lytic Group, copyright, 2021*****************/

/****************Published January, 2021 *****************/

/*********from http://www.lyticgroup.com by Edward Heraux*****************/



/****** Object:  StoredProcedure [dbo].[uspUpdateOldStatistics2012]    Script Date: 1/15/2021 11:59:17 AM ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO


--*****************************************************************************************************************

--If part of your database administration routine is to manually update statistics for tables that need it,

--run this script on a daily schedule instead. 

--This stored procedure will minimize guesswork and research time, as you can set an ideal number of days 

--after which you'd like any tables' statistics updated,

--and only update those tables.


This version of the script will work with SQL Server version 2008 RS _WITH SP2_ and up.

--*****************************************************************************************************************



/****************IMPORTANT WARNING!!!!!*****************************************************

 DO NOT run this script in the middle of your production day. 

You risk locking up entire tables of your data at horribly inopportune times.

Remember also that updating statistics also causes stored procedures to recompile, 

which will likely slow their performance the next time they are run.

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



/****************WE’D LOVE YOUR FEEDBACK****************************************************************************

Email: ed@lyticgroup.com  

and connect with Ed Heraux (the script author) on LinkedIn, http://www.linkedin.com/pub/edward-heraux-pmp/1/826/730/

for announcements of all new scripts.

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





CREATE PROC [dbo].[uspUpdateOldStatistics2012] @TableName sysname=NULL --Leave this blank for the stored proc to *find* all the tables that need their statistics updated.

--Otherwise, indicate a table name

, @MinDaysOld int=0 -- Only update statistics whose last update was at least this many days ago



/*************Find or update all statistics for tables whose statistics haven't been up dated in a given set of days.***************/

AS

DECLARE @YoungestDateToUpdate date= DATEADD(DAY,(-1)* @MinDaysOld,CURRENT_TIMESTAMP)

DECLARE @SQLText varchar(MAX)


--If the TableName variable was skipped, retrieve statistics update data on all tables

DECLARE @StatsHistory TABLE(RowID int IDENTITY

,ObjectType varchar(20)

,ObjectID int

,StatsId int

,ObjectName sysname

,FilterDefinition varchar(100)

,LastStatisticsUpdate datetime

,NumRows int

,RowsSampled int

,AgeInDays int

)


--If a table was provided, check its statistics update history, and update if the last one was long enough ago

IF @TableName IS NOT NULL

BEGIN

--Make sure the passed table name is legitimate

IF NOT EXISTS (SELECT 0 

FROM sys.sysobjects 

WHERE Name=@TableName AND type IN ('U','S','V'))

BEGIN

RAISERROR('The table name passed in as a parameter is not a valid name of a table or view in this database.',16,1)

RETURN

END


--The sys.dm_db_stats_properties function is only available starting with SQL 2008 R2 **with SP2.** . 

--This is the principle difference between this version of the stored proc and the <2008R2 version, also 

INSERT INTO @StatsHistory(ObjectType,ObjectID, StatsID,ObjectName, FilterDefinition, LastStatisticsUpdate, NumRows,RowsSampled,  AgeInDays)

SELECT O.type AS ObjectType,sp.object_id, sp.stats_id, O.name, filter_definition, last_updated, rows, rows_sampled

,DateDiff(DAY,STATS_DATE(Stat.object_id, Stat.stats_id),CAST (CURRENT_TIMESTAMP AS DATE)) AS AgeInDays

FROM sys.stats AS stat 

INNER JOIN sys.sysobjects O

ON O.id=stat.object_id

CROSS APPLY sys.dm_db_stats_properties(stat.object_id, stat.stats_id) AS sp

WHERE stat.object_id = object_id(@TableName)

AND CAST(last_updated as DATE) <=@YoungestDateToUpdate


--Update the statistics on the table if it hasn't had them updated recently enough. Throw an error if it hasn't.

IF @@ROWCOUNT>0

BEGIN

SET @SQLText= 'UPDATE STATISTICS [' + @TableName + '] WITH RESAMPLE' --Change this last item to FULLSCAN or SAMPLE X PERCENT if you have a  preference (see "UPDATE STATISTICS" in Books Online)

EXECUTE(@SQLText)

END

ELSE 

RAISERROR ('There were no tables with statistics last updated long enough ago.',16,1)


RETURN

END


--Otherwise, grab statistics for all tables that qualify by age of their last stats update

ELSE

BEGIN

--Again, the sys.dm_db_stats_properties function is only available starting with SQL 2008 R2 **with SP2.**

INSERT INTO @StatsHistory(ObjectType,ObjectID, StatsID,ObjectName, FilterDefinition, LastStatisticsUpdate, NumRows,RowsSampled,  AgeInDays)

SELECT  O.type AS ObjectType,sp.object_id, sp.stats_id, O.name, filter_definition, last_updated, rows, rows_sampled

,DateDiff(DAY,STATS_DATE(Stat.object_id, Stat.stats_id),CAST (CURRENT_TIMESTAMP AS DATE)) AS AgeInDays

FROM sys.stats AS stat 

INNER JOIN sys.sysobjects O

ON O.id=stat.object_id

CROSS APPLY sys.dm_db_stats_properties(stat.object_id, stat.stats_id) AS sp

--Exclude system tables, although this is enitrely optional.

WHERE O.type IN ('U','V')

AND CAST(last_updated as DATE) <=@YoungestDateToUpdate


--Check whether there are any tables to work on. If there aren't, exit with an error.

IF @@ROWCOUNT=0

BEGIN

RAISERROR ('There were no tables with their statistics last updated long enough ago.',16,1)

RETURN

END


END



--Size up and save the most recent statistics updates. This gives us a table of unique table names to work with.

SELECT  ObjectID, ObjectType, ObjectName, MAX(CAST(LastStatisticsUpdate AS DATE)) AS LatestStatisticsUpdate, MAX(AgeInDays) AS AgeInDays

INTO #TargetObjects

FROM @StatsHIstory

GROUP BY ObjectID, ObjectType, ObjectName




--Cursor through all the tables in the table variable, updating the statistics in each

DECLARE @Counter as int=1, @NumTables int, @CurrentObjectID int



SELECT @NumTables=COUNT(*), @CurrentObjectID=MIN(ObjectID) 

FROM #TargetObjects


WHILE @Counter<=@NumTables

BEGIN

--grab one table name to work with

SELECT @TableName=MIN(ObjectName)

FROM #TargetObjects

SET @SQLText= 'UPDATE STATISTICS [' + @TableName + '] WITH RESAMPLE' --Change this last item to FULLSCAN or SAMPLE X PERCENT if you have a  preference (see "UPDATE STATISTICS" in Books Online)


EXECUTE(@SQLText)

SET @Counter=@Counter+1

DELETE #TargetObjects

WHERE ObjectName=@TableName

END


GO




/****************WE’D LOVE YOUR FEEDBACK****************************************************************************

Email info@lyticgroup.com  

and connect with Ed Heraux (the script author) on LinkedIn, http://www.linkedin.com/pub/edward-heraux-pmp/1/826/730/

for announcements of all new scripts.

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