-
Notifications
You must be signed in to change notification settings - Fork 46
/
Copy pathautomated index rebuild old.sql
332 lines (268 loc) · 10.9 KB
/
automated index rebuild old.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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
/* Automated index maint script
Added by William Assaf, Sparkhound 20140304
Modified by William Assaf, Sparkhound 20140313
Modified by Robert Bishop, Sparkhound 20170107 -- changed Version selection to be compatible
--Primary objective is to never perform an offline index rebuild unless it is the only option.
*/
--#TODO For each database:
--Change name after USE below
--Change name in ALTER ... BULK_LOGGED below
--Change name in ALTER ... FULL near end
USE WideWorldImporters --#TODO Change database name
GO
DECLARE @testmode bit
DECLARE @startwindow tinyint
DECLARE @endwindow tinyint
SELECT @testmode = 1 -- flip to 1 to run out of cycle, without actually executing any code.
SELECT @startwindow = 0 -- Range (0-23). 24-hour of the day. Ex: 4 = 4am, 16 = 4pm. 0 = midnight.
SELECT @endwindow = 23 -- Range (0-23). 24-hour of the day. Ex: 4 = 4am, 16 = 4pm. 0 = midnight.
SET XACT_ABORT ON;
BEGIN TRY
IF @testmode = 0
ALTER DATABASE wideWorldImporters SET RECOVERY BULK_LOGGED; --#TODO Change database name
SET NOCOUNT ON;
DECLARE @objectid int
, @indexid int
, @partitioncount bigint
, @schemaname sysname
, @objectname sysname
, @indexname sysname
, @partitionnum bigint
, @partitions bigint
, @frag float
, @command varchar(8000)
, @Can_Reorg bit
, @Can_RebuildOnline nvarchar(60)
, @Can_Compress bit
DECLARE @ProductVersion varchar(15)
DECLARE @ServerEdition varchar(30)
SET @ServerEdition = convert(varchar(30),(SELECT SERVERPROPERTY('Edition')))
SET @ProductVersion = convert(varchar(15),(SELECT SERVERPROPERTY('ProductVersion')))
-- ensure the temporary table does not exist
IF EXISTS (SELECT name FROM tempdb.sys.objects WHERE name like '%C__work_to_do%')
DROP TABLE #C__work_to_do;
CREATE TABLE #C__work_to_do
(
objectid int NOT NULL
, indexid int NOT NULL
, partitionnum int NOT NULL
, frag decimal(9,2) NOT NULL
, Can_Reorg bit NOT NULL
, Can_RebuildOnline bit NOT NULL
, Can_Compress bit NOT NULL
, indexname sysname NOT NULL,
PRIMARY KEY ( objectid, indexid, partitionnum )
)
-- conditionally select from the function, converting object and index IDs to names.
INSERT INTO #C__work_to_do
( objectid
, indexid
, partitionnum
, frag
, Can_Reorg
, Can_RebuildOnline
, Can_Compress
, indexname
)
SELECT
objectid = s.object_id
, indexid = s.index_id
, partitionnum = s.partition_number
, frag = max(s.avg_fragmentation_in_percent)
, Can_Reorg = i.ALLOW_PAGE_LOCKS --An index cannot be reorganized when ALLOW_PAGE_LOCKS is set to 0.
,Can_RebuildOnline =
CASE
WHEN A.index_id is not null and A.user_type_id is not null
THEN 0 -- Cannot do ONLINE REBUILDs with certain data types in the index (key or INCLUDE).
WHEN A.index_id is null and s.index_id <= 1 and A.user_type_id is not null
THEN 0 -- Cannot do ONLINE REBUILDs with certain data types in the index (key or INCLUDE).
WHEN i.type_desc in ('xml','spatial') THEN 0 -- Cannot do ONLINE REBUILDs for certain index types.
WHEN (left(@ProductVersion,2) >= 10 ) and (@ServerEdition like 'Developer%' or @ServerEdition like 'Enterprise%' )
THEN 1
ELSE 0
END
, Can_Compress = CASE WHEN
(left(@ProductVersion,2) >= 10 ) and (@ServerEdition like 'Developer%' or @ServerEdition like 'Enterprise%' )
THEN 1
ELSE 0
END
, indexname = i.name
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, 'LIMITED') s
inner join --select * from
sys.indexes i on s.object_id = i.object_id and s.index_id = i.index_id
left outer join
(
select
c.object_id, ic.index_id, user_type_id = COALESCE(t.user_type_id, null)
from sys.columns c
left outer join sys.index_columns ic
on ic.object_id = c.object_id
and ic.column_id = c.column_id
left outer join sys.types t
on c.user_type_id = t.user_type_id
WHERE
( t.name in (N'image', N'text', N'ntext', N'xml')
or (t.name in ('varchar', 'nvarchar', 'varbinary')
and c.max_length = -1 --indicates (MAX)
)
)
) A
on i.object_id = A.object_id
AND i.index_id = A.index_id
WHERE
1=1
AND s.avg_fragmentation_in_percent > 10.0
AND s.index_id > 0 --
AND s.page_count > 1280 --12800 pages is 100mb
and i.name is not null
group by
s.object_id
, s.index_id
, s.partition_number
, i.ALLOW_PAGE_LOCKS
, A.index_id
, A.user_type_id
, i.type_desc
, i.name;
-- Declare the cursor for the list of partitions to be processed.
DECLARE partitions CURSOR FAST_FORWARD FOR
SELECT * FROM #C__work_to_do;
-- Open the cursor.
OPEN partitions;
-- Loop through the partitions.
FETCH NEXT
FROM partitions
INTO @objectid, @indexid, @partitionnum, @frag, @Can_Reorg, @Can_RebuildOnline, @Can_Compress, @indexname;
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
DECLARE @currenthour int = datepart(hour, Getdate())
IF(@startwindow > @endwindow -- wraps midnight
AND ( @currenthour >= @startwindow
OR (@currenthour <= @startwindow
AND @currenthour < @endwindow)
)
)
OR
(@startwindow <= @endwindow -- AM only or PM only
AND @currenthour >= @startwindow
and @currenthour < @endwindow
)
OR
@testmode = 1
BEGIN
SELECT @objectname = o.name
, @schemaname = s.name
FROM sys.objects AS o
INNER JOIN sys.schemas as s ON s.schema_id = o.schema_id
WHERE o.object_id = @objectid;
--SELECT @objectid, @indexid, @partitionnum, @frag, @Can_Reorg, @Can_RebuildOnline, @indexname, @objectname, @schemaname
SELECT @partitioncount = count (*)
FROM sys.partitions
WHERE object_id = @objectid AND index_id = @indexid;
SELECT @command = '';
-- 30 is an arbitrary decision point at which to switch between reorganizing and rebuilding
IF @frag < 30.0 and @Can_Reorg = 1
BEGIN
--print '0';
SELECT @command = 'ALTER INDEX [' + @indexname + '] ON [' + @schemaname + '].[' + @objectname + '] REORGANIZE ';
IF @partitioncount > 1
SELECT @command = @command + ' PARTITION=' + CONVERT (CHAR, @partitionnum);
SELECT @command = @command + '; UPDATE STATISTICS [' + @schemaname + '].[' + @objectname + '] ([' + @indexname + ']); '
--PRINT 'FragLevel ' + str(@FRAG)
END
IF @frag >= 60.0
BEGIN
--Doing an INDEX REBUILD with ONLINE = ON reduces the impact of locking to the production server,
-- though it will still create resource contention by keeping the drives and tempdb busy.
-- Unlike REORGANIZE steps, a REBUILD also updates the STATISTICS of an index.
IF @Can_RebuildOnline = 1
BEGIN
--print '1'
SELECT @command = 'ALTER INDEX [' + @indexname +'] ON [' + @schemaname + '].[' + @objectname + '] REBUILD ';
IF @partitioncount > 1
SELECT @command = @command + ' PARTITION=' + CONVERT (CHAR, @partitionnum);
SELECT @command = @command + ' WITH (ONLINE = ON'
IF @Can_Compress = 1
SELECT @command = @command + ', DATA_COMPRESSION = PAGE'
select @command = @command + ');'
END
--REORGANIZE processes are always ONLINE and are less intense than REBUILDs.
ELSE IF @Can_Reorg = 1
BEGIN
--print '2'
SELECT @command = 'ALTER INDEX [' + @indexname +'] ON [' + @schemaname + '].[' + @objectname + '] REORGANIZE ';
IF @partitioncount > 1
SELECT @command = @command + ' PARTITION=' + CONVERT (CHAR, @partitionnum);
SELECT @command = @command + '; UPDATE STATISTICS [' + @schemaname + '].[' + @objectname + '] ([' + @indexname + ']); '
END
--Only do a full, offline index rebuild in the middle of the night.
ELSE IF datepart(hour, Getdate()) < 3 --inclusive both hours.
BEGIN
--print '3'
SELECT @command = 'ALTER INDEX [' + @indexname + '] ON [' + @schemaname + '.' + @objectname + '] REBUILD';
IF @partitioncount > 1
SELECT @command = @command + ' PARTITION = ' + CONVERT (CHAR, @partitionnum);
IF @Can_Compress = 1
SELECT @command = @command + ' WITH (DATA_COMPRESSION = PAGE); '
--PRINT 'FragLevel ' + str(@FRAG)
END
END
IF @command <> ''
BEGIN
INSERT INTO DBALogging.dbo.IndexMaintLog (CurrentDatabase, Command, ObjectName, BeginTimeStamp, StartWindow, EndWindow, TestMode)
SELECT DB_NAME(), @Command, '[' + DB_Name() + '].[' + @objectname + '].[' + @schemaname + ']', getdate(), @StartWindow, @EndWindow, @TestMode
BEGIN TRY
IF @testmode = 0 EXEC (@command);
PRINT N'Executed: ' + @command
UPDATE DBALogging.dbo.IndexMaintLog
SET EndTimeStamp = Getdate()
, Duration_s = datediff(s, BeginTimeStamp, getdate())
where id = SCOPE_IDENTITY() and EndTimeStamp is null
END TRY
BEGIN CATCH
Print N'Error: ' + ERROR_MESSAGE()
UPDATE DBALogging.dbo.IndexMaintLog
SET ErrorMessage = cast(ERROR_NUMBER() as char(9)) + ERROR_MESSAGE()
where id = SCOPE_IDENTITY() and EndTimeStamp is null
END CATCH
END
END
END TRY
BEGIN CATCH
PRINT N'Failed to execute the command: ' + @command + N' Error Message: ' + ERROR_MESSAGE()
END CATCH
FETCH NEXT FROM partitions
INTO @objectid, @indexid, @partitionnum, @frag, @Can_Reorg, @Can_RebuildOnline, @Can_Compress, @indexname;
END
-- Close and deallocate the cursor.
CLOSE partitions;
DEALLOCATE partitions;
-- drop the temporary table
IF EXISTS (SELECT name FROM tempdb.sys.objects WHERE name like '%C__work_to_do%')
DROP TABLE #C__work_to_do;
IF @testmode = 0
ALTER DATABASE [WideWorldImporters] SET RECOVERY FULL --#TODO Change database name
END TRY
BEGIN CATCH
INSERT INTO DBALogging.dbo.IndexMaintLog (CurrentDatabase, ErrorMessage , BeginTimeStamp, TestMode)
SELECT DB_NAME(), cast(ERROR_NUMBER() as char(9)) + ERROR_MESSAGE(), getdate(), @TestMode
END CATCH
GO
/*
DROP TABLE DBALogging.dbo.IndexMaintLog;
CREATE TABLE DBALogging.dbo.IndexMaintLog
( id int not null identity(1,1) PRIMARY KEY
, CurrentDatabase sysname not null DEFAULT (DB_NAME())
, Command nvarchar(1000) null
, ObjectName nvarchar(100) null
, BeginTimeStamp datetime null
, TestMode bit null
, StartWindow tinyint null
, EndWindow tinyint null
, EndTimeStamp datetime null
, Duration_s int null
, ErrorMessage nvarchar(4000) null
)
select * from DBALogging.dbo.indexmaintlog
*/