-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmsdb.dbo.cs_CUSTOM_D2L_StatsUpdate.sql
251 lines (223 loc) · 8.04 KB
/
msdb.dbo.cs_CUSTOM_D2L_StatsUpdate.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
use msdb;
GO
ALTER PROCEDURE dbo.cs_CUSTOM_D2L_StatsUpdate
@DBName SYSNAME,
@JobName SYSNAME = 'IT Update Stats'
AS
SET NOCOUNT ON
--Number of rows modified threshold to trigger stats update in combination with
-- "@stats_age_threshold_days" of days since last update
DECLARE @stats_rows_changed_threshold INT;
SELECT TOP 1 @stats_rows_changed_threshold = CAST(ConfigValue AS INT)
FROM msdb.dbo.D2LJobsConfigValues
WHERE DatabaseName IN (@DBName, 'DefaultValue')
AND JobName = @JobName
AND ConfigName = 'stats_rows_changed_threshold'
ORDER BY CASE WHEN DatabaseName = @DBName THEN 0 ELSE 1 END ASC;
--Number of days since last update to trigger stats update in combination with
-- @stats_rows_changed_threshold" rows changed
DECLARE @stats_age_threshold_days INT;
SELECT TOP 1 @stats_age_threshold_days = CAST(ConfigValue AS INT)
FROM msdb.dbo.D2LJobsConfigValues
WHERE DatabaseName IN (@DBName, 'DefaultValue')
AND JobName = @JobName
AND ConfigName = 'stats_age_threshold_days'
ORDER BY CASE WHEN DatabaseName = @DBName THEN 0 ELSE 1 END ASC;
--Number of rows to determine if FULL SAMPLE scan should be used. Less than
-- this value means FULL SAMPLE otherwise let SQL Server determine default
-- sample rate
DECLARE @stats_rows_count_threshold INT;
SELECT TOP 1 @stats_rows_count_threshold = CAST(ConfigValue AS INT)
FROM msdb.dbo.D2LJobsConfigValues
WHERE DatabaseName IN (@DBName, 'DefaultValue')
AND JobName = @JobName
AND ConfigName = 'stats_rows_count_threshold'
ORDER BY CASE WHEN DatabaseName = @DBName THEN 0 ELSE 1 END ASC;
--Keep stats update history for this many days
DECLARE @stats_HistoryKeepThreshold INT;
SELECT TOP 1 @stats_HistoryKeepThreshold = CAST(ConfigValue AS INT)
FROM msdb.dbo.D2LJobsConfigValues
WHERE DatabaseName IN (@DBName, 'DefaultValue')
AND JobName = @JobName
AND ConfigName = 'stats_HistoryKeepThreshold'
ORDER BY CASE WHEN DatabaseName = @DBName THEN 0 ELSE 1 END ASC;
--This is a default sample rate/percentage we use for any stats created on a
-- table with more than @stats_rows_count_threshold " rows changed
DECLARE @default_stats_sample_size INT;
SELECT TOP 1 @default_stats_sample_size = CAST(ConfigValue AS INT)
FROM msdb.dbo.D2LJobsConfigValues
WHERE DatabaseName IN (@DBName, 'DefaultValue')
AND JobName = @JobName
AND ConfigName = 'default_stats_sample_size'
ORDER BY CASE WHEN DatabaseName = @DBName THEN 0 ELSE 1 END ASC;
DECLARE @CollectStatsCmd NVARCHAR(MAX)
DECLARE @ErrorMessage NVARCHAR(MAX)
DECLARE @CurrentUpdateStatsCmd NVARCHAR(MAX)
-- Verify DB exists....
IF NOT EXISTS(SELECT name FROM sys.databases WHERE name = @DBName)
BEGIN
GOTO ERROR_EXIT_HERE
END
------------------------------------------------------------------------------------
---- Save progress of the completed actions (to MSDN database)
------------------------------------------------------------------------------------
IF (OBJECT_ID('msdb.dbo.D2LStatsUpdateHistory') IS NULL)
BEGIN
CREATE TABLE msdb.dbo.D2LStatsUpdateHistory
(
DatabaseName sysname,
DatabaseId int,
ObjectId int,
TableName sysname NULL,
TableSchema sysname NULL,
StatsId int,
StatsName sysname,
StatsLastUpdated DATETIME,
RowsCount bigint,
RowsModifiedCount bigint,
RowsSampledCount bigint,
Action nvarchar(max) NULL,
TimeStarted DATETIME NULL,
TimeCompleted DATETIME NULL
)
END;
--------------Maintenance-------------------------------------
DELETE FROM msdb.dbo.D2LStatsUpdateHistory
WHERE DatabaseName = @DBName
AND TimeCompleted < DATEADD(DD, -@stats_HistoryKeepThreshold, GETDATE())
--------------------------------------------------------------
--------------------------------------------------------------
----Load D2LStatsUpdateHistory with Stats Metadata
--------------------------------------------------------------
SET @CollectStatsCmd = 'USE ' + @DBName + ';
INSERT msdb.dbo.D2LStatsUpdateHistory (
DatabaseName,
DatabaseId,
ObjectId,
TableName,
TableSchema,
StatsId,
StatsName,
StatsLastUpdated,
RowsCount,
RowsModifiedCount,
RowsSampledCount )
SELECT @DBName,
DB_ID(@DBName),
s.object_id,
OBJECT_NAME(s.object_id),
OBJECT_SCHEMA_NAME(s.object_id),
s.stats_id,
s.name,
sp.last_updated,
sp.rows,
sp.modification_counter,
sp.rows_sampled
FROM sys.stats s
CROSS APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
WHERE OBJECTPROPERTY(s.object_id,''IsMSShipped'') = 0
AND sp.last_updated < DATEADD(DD, -@stats_age_threshold_days, GETDATE())
AND sp.modification_counter >= @stats_rows_changed_threshold
AND s.name not in (
SELECT StatsName
FROM msdb.dbo.D2LStatsUpdateHistory
WHERE TimeCompleted IS NULL
AND DatabaseName = @DBName
)';
exec sp_executesql @stmt = @CollectStatsCmd,
@params = N'@DBName sysname,
@stats_age_threshold_days int,
@stats_rows_changed_threshold int',
@DBName = @DBName,
@stats_age_threshold_days = @stats_age_threshold_days,
@stats_rows_changed_threshold = @stats_rows_changed_threshold;
-----------------Process D2LStatsUpdateHistory Table------------------
DECLARE @SchemaN SYSNAME
DECLARE @TableN SYSNAME
DECLARE @StatsId INT
DECLARE @StatsN SYSNAME
DECLARE @ObjectId INT
DECLARE @RowsC BIGINT
DECLARE @TimeStartedTemp DATETIME
DECLARE cStats CURSOR FOR
SELECT TableName,
TableSchema,
ObjectId,
StatsId,
StatsName,
RowsCount
FROM msdb.dbo.D2LStatsUpdateHistory
WHERE DatabaseName = @DBName
AND TimeCompleted IS NULL
ORDER BY StatsLastUpdated DESC;
OPEN cStats;
FETCH NEXT FROM cStats
INTO @TableN,
@SchemaN,
@ObjectId ,
@StatsId,
@StatsN,
@RowsC;
WHILE ( @@FETCH_STATUS = 0 )
BEGIN
IF (@RowsC > @stats_rows_count_threshold AND NOT EXISTS(SELECT 1 FROM sys.Indexes WHERE object_id = @ObjectId))
BEGIN
SET @CurrentUpdateStatsCmd = 'USE ' + @DBName + ';
UPDATE STATISTICS '
+ QUOTENAME(@SchemaN) + '.'
+ QUOTENAME(@TableN)
+ '(' + QUOTENAME(@StatsN) + ');';
END
ELSE IF (@RowsC > @stats_rows_count_threshold AND EXISTS(SELECT 1 FROM sys.Indexes WHERE object_id = @ObjectId))
BEGIN
SET @CurrentUpdateStatsCmd = 'USE ' + @DBName + ';
UPDATE STATISTICS '
+ QUOTENAME(@SchemaN) + '.'
+ QUOTENAME(@TableN)
+ '(' + QUOTENAME(@StatsN) + ')
WITH SAMPLE ' + CAST(@default_stats_sample_size AS NVARCHAR(5)) + ' PERCENT;';
END
ELSE
BEGIN
SET @CurrentUpdateStatsCmd = 'USE ' + @DBName + ';
UPDATE STATISTICS '
+ QUOTENAME(@SchemaN) + '.'
+ QUOTENAME(@TableN)
+ '(' + QUOTENAME(@StatsN) + ')
WITH SAMPLE 100 PERCENT;';
END
--Update the start time
UPDATE msdb.dbo.D2LStatsUpdateHistory
SET TimeStarted = GETDATE(),
Action = @CurrentUpdateStatsCmd
WHERE TableSchema = @SchemaN
AND TableName = @TableN
AND StatsName = @StatsN
AND DatabaseName = @DBName
AND ObjectId = @ObjectId;
EXEC sp_executesql @CurrentUpdateStatsCmd;
--Update the time completed
UPDATE msdb.dbo.D2LStatsUpdateHistory
SET TimeCompleted = GETDATE()
WHERE TableSchema = @SchemaN
AND TableName = @TableN
AND StatsName = @StatsN
AND DatabaseName = @DBName
AND ObjectId = @ObjectId;
FETCH NEXT FROM cStats
INTO @TableN,
@SchemaN,
@ObjectId ,
@StatsId,
@StatsN,
@RowsC;
END
CLOSE cStats
DEALLOCATE cStats
GOTO SUCCESS_EXIT_HERE
ERROR_EXIT_HERE:
SELECT @ErrorMessage = 'The DB called "' + @DBName + '" doesn''t exist!'
RAISERROR (@ErrorMessage, 10, 1) WITH LOG, NOWAIT --informational only
SUCCESS_EXIT_HERE:
SELECT @ErrorMessage = 'All done!'
RAISERROR (@ErrorMessage, 10, 1) WITH LOG, NOWAIT --informational only