Update Statistics By Age
Rather than update statistics on a table manually, which you'll usually do when its performance has already been lagging, run this stored procedure on a schedule to update the statistics that have not been updated in a number of days. Optionally, you may pass the name of a specific table.
SCRIPT:
Update Statistics By Age
/****************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. ************************************************************************************************************************/