forked from DBAChad/qs-automation
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathQueryStoreAutomation_TestScript.sql
More file actions
597 lines (468 loc) · 24.1 KB
/
QueryStoreAutomation_TestScript.sql
File metadata and controls
597 lines (468 loc) · 24.1 KB
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
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
/*
TODO: Before going to production, change all the email addresses to be ascenddbadmins@henryschein.com
TODO: Change all the scripts to create job steps.
Tests are idempotent, meaning they can be run any number of times and in any order.
If one test fails, it may leave the DB in an inconsistent state though, so check for that.
*/
/**************************************************************************************************
Pre-test set up data and objects
*************************************************************************************************/
--Set up some bad data
DROP TABLE IF EXISTS QSAutomation.BigData
GO
CREATE TABLE QSAutomation.BigData (col1 char(1600), col2 INT, col3 char(1600))
GO
INSERT INTO QSAutomation.BigData VALUES ('abcdefghijklmn', RAND() * 100, REPLICATE('a', RAND() * 1000))
GO
INSERT INTO QSAutomation.BigData
SELECT *
FROM QSAutomation.BigData
GO 17
INSERT INTO QSAutomation.BigData VALUES ('opqrstuvwxyz', RAND() * 100, REPLICATE('a', RAND() * 1000))
GO
CREATE NONCLUSTERED INDEX IX_BigData_Col1 ON QSAutomation.BigData(Col1)
GO
CREATE OR ALTER PROCEDURE QSAutomation.BadPlan @Search varchar(20)
AS
BEGIN
SELECT count(*) FROM QSAutomation.BigData WHERE Col1 = @Search
END
GO
CREATE OR ALTER PROCEDURE QSAutomation.ResetQueryStore
AS
BEGIN
DECLARE @QueryID BIGINT
, @PlanID BIGINT
DELETE FROM QSAutomation.Query WHERE QueryHash IN (
0x766DA712B668299F --High Variation query
, 0x26E3F4EAD27D7C86 --Pulled from manually pinned plans
, 0x3982C7BE77A0D5B7 --Long running mono-plan query
)
IF EXISTS (SELECT 1
FROM sys.query_store_query
INNER JOIN sys.query_store_plan ON query_store_query.query_id = query_store_plan.query_id
WHERE query_hash IN (
0x766DA712B668299F --High Variation query
, 0x26E3F4EAD27D7C86 --Pulled from manually pinned plans
, 0x3982C7BE77A0D5B7 --Long running mono-plan query
)
AND is_forced_plan = 1)
BEGIN
SELECT @QueryID = query_store_query.query_id
, @PlanID = query_store_plan.plan_id
FROM sys.query_store_query
INNER JOIN sys.query_store_plan ON query_store_query.query_id = query_store_plan.query_id
WHERE query_hash IN (
0x766DA712B668299F --High Variation query
, 0x26E3F4EAD27D7C86 --Pulled from manually pinned plans
, 0x3982C7BE77A0D5B7 --Long running mono-plan query
)
AND is_forced_plan = 1
EXEC sp_query_store_unforce_plan @QueryID, @PlanID
END
IF EXISTS (SELECT 1
FROM sys.query_store_query
WHERE query_hash IN (
0x766DA712B668299F --High Variation query
, 0x26E3F4EAD27D7C86 --Pulled from manually pinned plans
, 0x3982C7BE77A0D5B7 --Long running mono-plan query
))
BEGIN
SELECT @QueryID = query_store_query.query_id
FROM sys.query_store_query
WHERE query_hash IN (
0x766DA712B668299F --High Variation query
, 0x26E3F4EAD27D7C86 --Pulled from manually pinned plans
, 0x3982C7BE77A0D5B7 --Long running mono-plan query
)
EXEC sp_query_store_remove_query @QueryID
END
END
GO
/**************************************************************************************************
Step 0: Set up tables
*************************************************************************************************/
--Run the setup script 3 times to validate that it executes, and if the script has already been run that it still executes ok
/**************************************************************************************************
Step 1: High Variation Check
*************************************************************************************************/
--Lower the threshold for testing:
UPDATE QSAutomation.Configuration
SET ConfigurationValue = '10' WHERE ConfigurationName = 't-Statistic Threshold'
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
--Set up competing plans
DBCC FREEPROCCACHE
GO
EXEC QSAutomation.BadPlan 'opqrstuvwxyz'
GO 200
DBCC FREEPROCCACHE
GO
EXEC QSAutomation.BadPlan 'abcdefghijklmn'
GO 200
--Run Step 1 here
--It should identify the better plan and pin it.
EXEC QSAutomation.QueryStore_HighVariationCheck
select * from QSAutomation.Query
select * from sys.query_store_plan where is_forced_plan = 1
--Reset the threshold for testing:
UPDATE QSAutomation.Configuration
SET ConfigurationValue = '100' WHERE ConfigurationName = 't-Statistic Threshold'
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
/**************************************************************************************************
Step 2: Check for invalid plans
*************************************************************************************************/
--Lower the threshold for testing:
UPDATE QSAutomation.Configuration
SET ConfigurationValue = '10' WHERE ConfigurationName = 't-Statistic Threshold'
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
--We start basically the same as the Step 1 test
--Set up competing plans
DBCC FREEPROCCACHE
GO
EXEC QSAutomation.BadPlan 'opqrstuvwxyz'
GO 200
DBCC FREEPROCCACHE
GO
EXEC QSAutomation.BadPlan 'abcdefghijklmn'
GO 200
--Run Step 1 here
--It should identify the better plan and pin it.
EXEC QSAutomation.QueryStore_HighVariationCheck
DROP INDEX QSAutomation.BigData.IX_BigData_Col1
GO
--You may have to wait for the data flush or stats collection interval (or both).
DBCC FREEPROCCACHE
GO
EXEC QSAutomation.BadPlan 'abcdefghijklmn'
select force_failure_count, last_force_failure_reason, last_force_failure_reason_desc, * from sys.query_store_plan where is_forced_plan = 1
--Run Step 2 here
EXEC QSAutomation.QueryStore_InvalidPlanCheck
select * from QSAutomation.Query --Query is gone
select * from sys.query_store_plan where is_forced_plan = 1 --Query is gone
select * from QSAutomation.ActivityLog --shows log messages
--Reset the index
CREATE NONCLUSTERED INDEX IX_BigData_Col1 ON QSAutomation.BigData(Col1)
GO
--Reset the threshold for testing:
UPDATE QSAutomation.Configuration
SET ConfigurationValue = '100' WHERE ConfigurationName = 't-Statistic Threshold'
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
/**************************************************************************************************
Step 3: Check for better plans
*************************************************************************************************/
--Lower the threshold for testing:
UPDATE QSAutomation.Configuration
SET ConfigurationValue = '5' WHERE ConfigurationName = 't-Statistic Threshold'
UPDATE QSAutomation.Configuration
SET ConfigurationValue = '0' WHERE ConfigurationName = 'High Variation Duration Threshold (MS)'
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
--Replace our index with one that is slightly less efficient.
DROP INDEX QSAutomation.BigData.IX_BigData_Col1
GO
CREATE NONCLUSTERED INDEX IX_BigData_Col1_Col2 ON QSAutomation.BigData(Col1, Col2)
GO
--Set up competing plans (just like step 1 tests, but without the index)
DBCC FREEPROCCACHE
GO
EXEC QSAutomation.BadPlan 'opqrstuvwxyz'
GO 200
DBCC FREEPROCCACHE
GO
EXEC QSAutomation.BadPlan 'abcdefghijklmn'
GO 200
--Run Step 1 here
EXEC QSAutomation.QueryStore_HighVariationCheck
select * from QSAutomation.Query --Note the plan hash, probably 0x872D21FCBEA49106
select * from sys.query_store_plan where is_forced_plan = 1
--Run Step 3
--Nothing should happen (because it hasn't been more than 1 day)
EXEC QSAutomation.QueryStore_BetterPlanCheck @AlwaysCheck = 1 --Ignore the "9-2" requirement
UPDATE QSAutomation.Query SET QueryCreationDatetime = DATEADD(DAY, -1, QueryCreationDatetime) WHERE QueryHash IN (0x766DA712B668299F)
--Run Step 3. It should "unpin" and start looking for a better plan
EXEC QSAutomation.QueryStore_BetterPlanCheck @AlwaysCheck = 1 --Ignore the "9-2" requirement
SELECT * FROM QSAutomation.Query --StatusID 11
SELECT * FROM QSAutomation.Configuration --Query unlock start time
--Run Step 3 again
--Nothing should happen (it stays unlocked for 10 minutes and it hasn't been that long yet)
EXEC QSAutomation.QueryStore_BetterPlanCheck @AlwaysCheck = 1 --Ignore the "9-2" requirement
--Create a new, better plan with an index, drop the inferior index
CREATE NONCLUSTERED INDEX IX_BigData_Col1 ON QSAutomation.BigData(Col1)
DROP INDEX QSAutomation.BigData.IX_BigData_Col1_Col2
GO
DBCC FREEPROCCACHE
GO
EXEC QSAutomation.BadPlan 'opqrstuvwxyz'
GO 200
DBCC FREEPROCCACHE
GO
EXEC QSAutomation.BadPlan 'abcdefghijklmn'
GO 200
--Run Step 1 (it shouldn't do anything, ignoring this because it is unlocked)
EXEC QSAutomation.QueryStore_HighVariationCheck
UPDATE QSAutomation.Configuration
SET ConfigurationValue = DATEADD(MINUTE, -10, CONVERT(DATETIME2(7), ConfigurationValue))
WHERE ConfigurationName = 'Query Unlock Start Time'
--Run Step 1 (it still shouldn't do anything, it ignores unlocked queries)
EXEC QSAutomation.QueryStore_HighVariationCheck
--Run Step 3 (it should pick up the better plan)
EXEC QSAutomation.QueryStore_BetterPlanCheck @AlwaysCheck = 1 --Ignore the "9-2" requirement
SELECT * FROM QSAutomation.Query --Note the plan hash, probably different than before.
SELECT * FROM QSAutomation.Configuration
--Reset the threshold for testing:
UPDATE QSAutomation.Configuration
SET ConfigurationValue = '100' WHERE ConfigurationName = 't-Statistic Threshold'
UPDATE QSAutomation.Configuration
SET ConfigurationValue = '500' WHERE ConfigurationName = 'High Variation Duration Threshold (MS)'
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
/**************************************************************************************************
Step 4: Clean Plan Cache
*************************************************************************************************/
--Lower the threshold for testing:
UPDATE QSAutomation.Configuration
SET ConfigurationValue = '10' WHERE ConfigurationName = 't-Statistic Threshold'
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
DBCC FREEPROCCACHE
GO
/**********************************************************************************************
Setup a "status 11"
*********************************************************************************************/
EXEC QSAutomation.BadPlan 'opqrstuvwxyz'
GO 200
DBCC FREEPROCCACHE
GO
EXEC QSAutomation.BadPlan 'abcdefghijklmn'
GO 200
--Run Step 1 here, it should identify the better plan and pin it.
EXEC QSAutomation.QueryStore_HighVariationCheck
--Make it appear that it's time for this query to to be re-evaluated.
UPDATE QSAutomation.Query SET QueryCreationDatetime = DATEADD(DAY, -1, QueryCreationDatetime) WHERE QueryHash = 0x766DA712B668299F
--Run Step 3
EXEC QSAutomation.QueryStore_BetterPlanCheck @AlwaysCheck = 1 --Disabled the 9-5 check
--This sets the Plan to StatusID 11, which will have it's plans evicted from the cache
/**********************************************************************************************
Setup a "status 30"
*********************************************************************************************/
--Put the query into sp_executeSQL so we don't have any inadvertant whitespace or other changes (and we know the hash that comes out)
EXEC sp_executeSQL N'SELECT TOP 10 * FROM QSAutomation.BigData WHERE col2 = 10 ORDER BY col3'
EXEC sp_executeSQL N'SELECT TOP 10 * FROM QSAutomation.BigData WHERE col2 = 10 ORDER BY col3'
DECLARE @query_id int
, @plan_id INT
SELECT @query_id = query_store_query.query_id
, @plan_id = plan_id
FROM sys.query_store_query
INNER JOIN sys.query_store_plan ON sys.query_store_query.query_id = query_store_plan.query_id
WHERE query_hash = 0x26E3F4EAD27D7C86
IF @query_id IS NULL OR @plan_id IS NULL
BEGIN
THROW 50000, 'Whoops, didn''t find the plan', 1
END
EXEC sp_query_store_force_plan @query_id = @query_id, @plan_id = @plan_id
--Run step 7 (to pull this into the QSAutomation tables)
EXEC QSAutomation.QueryStore_IncludeManuallyPinnedPlans
--Update the plan to be "Always unlocked"
UPDATE QSAutomation.Query SET StatusID = 30 WHERE QueryHash = 0x26E3F4EAD27D7C86
/**********************************************************************************************
Setup a "status 20"
*********************************************************************************************/
--Put the query into sp_executeSQL so we don't have any inadvertant whitespace or other changes (and we know the hash that comes out)
--I had to run these two at a time (running them all together was too much)
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
GO
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
GO
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
GO
--Run step 5 (to pick up the mono-plan long query)
EXEC QSAutomation.QueryStore_PoorPerformingMonoPlanCheck
--Query Hash: 0x3982C7BE77A0D5B7
-- plan hash: 0x695BC94B7C750225
--How many plans are in the cache? There should be at least three, one for each fo the three we set up above.
--I did see some very strange edge cases where sometimes a plan in the cache had the Plan Hash in the Query Hash field (i.e. the same hash for both values
--and it was the plan's hash). I'm not sure what causes this and it wasn't always reproducable (internet suggests it's from plan guides, but we're not using one).
--(-http://blog.sqlgrease.com/query_hash-query_plan_hash-useful/)
SELECT Query.QueryID
, Query.StatusID
, query_plan.value('declare namespace QP="http://schemas.microsoft.com/sqlserver/2004/07/showplan"; (//QP:StmtSimple/@QueryHash)[1]', 'varchar(50)')
, *
FROM sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_query_plan(dm_exec_cached_plans.plan_handle) AS qp
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS st
INNER JOIN QSAutomation.Query ON query_plan.value('declare namespace QP="http://schemas.microsoft.com/sqlserver/2004/07/showplan"; (//QP:StmtSimple/@QueryHash)[1]', 'varchar(50)') = CONVERT(VARCHAR(50), Query.QueryHash, 1)
WHERE (
StatusID / 10 = 1
OR StatusID IN (20, 30)
)
--Clear the plans, then see that they are no longer in cache
EXEC QSAutomation.QueryStore_ClearPlansFromCache
SELECT Query.QueryID
, Query.StatusID
, query_plan.value('declare namespace QP="http://schemas.microsoft.com/sqlserver/2004/07/showplan"; (//QP:StmtSimple/@QueryHash)[1]', 'varchar(50)')
, *
FROM sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_query_plan(dm_exec_cached_plans.plan_handle) AS qp
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS st
INNER JOIN QSAutomation.Query ON query_plan.value('declare namespace QP="http://schemas.microsoft.com/sqlserver/2004/07/showplan"; (//QP:StmtSimple/@QueryHash)[1]', 'varchar(50)') = CONVERT(VARCHAR(50), Query.QueryHash, 1)
WHERE (
StatusID / 10 = 1
OR StatusID IN (20, 30)
)
--Reset the threshold for testing:
UPDATE QSAutomation.Configuration
SET ConfigurationValue = '100' WHERE ConfigurationName = 't-Statistic Threshold'
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
/**************************************************************************************************
Step 5: Poor Performing Mono-plan Check
*************************************************************************************************/
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
--Put the query into sp_executeSQL so we don't have any inadvertant whitespace or other changes (and we know the hash that comes out)
--I had to run these two at a time (it was a lot of data for SSMS to process).
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
GO
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
GO
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
EXEC sp_executeSQL N'WITH BD AS (SELECT top 300 * FROM QSAutomation.BigData), BD2 AS (SELECT TOP 300 * FROM QSAutomation.BigData) SELECT * FROM BD, BD2'
--Run step 5 (to pick up the mono-plan long query)
EXEC QSAutomation.QueryStore_PoorPerformingMonoPlanCheck
--Should be StatusID:20
SELECT *
FROM QSAutomation.Query
WHERE QueryHash = 0x3982C7BE77A0D5B7
--Make it look like it was logged 90 minutes ago
UPDATE QSAutomation.Query
SET QueryCreationDatetime = DATEADD(MINUTE, -90, QueryCreationDatetime)
WHERE QueryHash = 0x3982C7BE77A0D5B7
--Run step 5 (to indicate failure finding a better plan)
EXEC QSAutomation.QueryStore_PoorPerformingMonoPlanCheck
--Should be StatusID:0
SELECT *
FROM QSAutomation.Query
WHERE QueryHash = 0x3982C7BE77A0D5B7
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
/**************************************************************************************************
Step 6: Fix a Broken Query Store
For obvious reasons (I hope), don't test this on production.
*************************************************************************************************/
DROP TABLE IF EXISTS #RememberQSMaxSize
CREATE TABLE #RememberQSMaxSize (MaxSize bigint)
INSERT INTO #RememberQSMaxSize
SELECT max_storage_size_mb
FROM sys.database_query_store_options
/**********************************************************************************************
Check just a regular read_only issue
*********************************************************************************************/
DECLARE @DynamicSQL nvarchar(max)
SELECT @DynamicSQL = 'ALTER DATABASE ' + DB_NAME() + ' SET QUERY_STORE (OPERATION_MODE = READ_ONLY)'
EXEC sp_executesql @DynamicSQL
SELECT * FROM sys.database_query_store_options
--Execute Step 6
EXEC QSAutomation.QueryStore_FixBrokenQueryStore
SELECT * FROM sys.database_query_store_options
/**********************************************************************************************
Create an out-of-space condition
*********************************************************************************************/
DECLARE @DynamicSQL nvarchar(max)
SELECT @DynamicSQL = 'ALTER DATABASE ' + DB_NAME() + ' SET QUERY_STORE = ON (MAX_STORAGE_SIZE_MB = ' + CONVERT(nvarchar(max), (IIF(current_storage_size_mb = 1, 1, current_storage_size_mb - 1))) + ')'
FROM sys.database_query_store_options
EXEC sp_executesql @DynamicSQL
--Wait until it goes read-only. Sometimes this is hard to trigger deliberately.
--May take awhile - perhaps even flush_interval_seconds (which defaults to 15 minutes).
--Also, it seems like some kind of activity is necessary, let's create a bunch of unique queries
DECLARE @X int = 1
, @DynamicSQL nvarchar(max)
WHILE @X < 500
BEGIN
SELECT @DynamicSQL = 'SELECT TOP 10 ' + CONVERT(varchar(10), @X) + ', * FROM QSAutomation.BigData WHERE Col1 = ''10'''
, @X = @X + 1
EXEC sp_executesql @DynamicSQL
EXEC sp_executesql @DynamicSQL
END
SELECT max_storage_size_mb, * FROM sys.database_query_store_options
--Execute Step 6
EXEC QSAutomation.QueryStore_FixBrokenQueryStore
SELECT max_storage_size_mb, * FROM sys.database_query_store_options
--Reset the max size
DECLARE @DynamicSQL nvarchar(max)
SELECT @DynamicSQL = 'ALTER DATABASE ' + DB_NAME() + ' SET QUERY_STORE = ON (MAX_STORAGE_SIZE_MB = ' + CONVERT(nvarchar(max), (MaxSize - 1)) + ')'
FROM #RememberQSMaxSize
EXEC sp_executesql @DynamicSQL
SELECT max_storage_size_mb, * FROM sys.database_query_store_options
/**********************************************************************************************
Validate that the counter resets after a cooldown
*********************************************************************************************/
SELECT *
FROM QSAutomation.Configuration
WHERE ConfigurationName IN ('Last Query Store Reset', 'Query Store Reset Count')
UPDATE QSAutomation.Configuration
SET ConfigurationValue = DATEADD(HOUR,-26, CONVERT(datetime2(7), ConfigurationValue))
WHERE ConfigurationName = 'Last Query Store Reset'
--Execute Step 6
EXEC QSAutomation.QueryStore_FixBrokenQueryStore
SELECT *
FROM QSAutomation.Configuration
WHERE ConfigurationName IN ('Last Query Store Reset', 'Query Store Reset Count')
/**************************************************************************************************
Step 7: Include Manually Pinned Plans
*************************************************************************************************/
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
--Put the query into sp_executeSQL so we don't have any inadvertant whitespace or other changes (and we know the hash that comes out)
EXEC sp_executeSQL N'SELECT TOP 10 * FROM QSAutomation.BigData WHERE col2 = 10 ORDER BY col3'
EXEC sp_executeSQL N'SELECT TOP 10 * FROM QSAutomation.BigData WHERE col2 = 10 ORDER BY col3'
DECLARE @query_id int
, @plan_id INT
SELECT @query_id = query_store_query.query_id
, @plan_id = plan_id
FROM sys.query_store_query
INNER JOIN sys.query_store_plan ON sys.query_store_query.query_id = query_store_plan.query_id
WHERE query_hash = 0x26E3F4EAD27D7C86
IF @query_id IS NULL OR @plan_id IS NULL
BEGIN
THROW 50000, 'Whoops, didn''t find the plan', 1
END
EXEC sp_query_store_force_plan @query_id = @query_id, @plan_id = @plan_id
--Run step 7 (to pull this into the QSAutomation tables)
EXEC QSAutomation.QueryStore_IncludeManuallyPinnedPlans
--Reset any plan info for the query we play with
EXEC QSAutomation.ResetQueryStore
/**************************************************************************************************
Step 8: Clean up unused plans
*************************************************************************************************/
--This is a difficult one to test since you can't directly manipulate the contents of the query
--store to change the record of how long something has been in there. The best you can do is
--check before running to see if anything _should_ be cleaned up and then verify that it was.
--But if nothing needs to be cleaned up, the test is inconclusive (i.e. doesn't tell you if the
--code worked or not.
--Is there anything to clean up?
SELECT query_id, plan_id
FROM sys.query_store_plan
WHERE is_forced_plan = 1
AND DATEDIFF(day, last_execution_time, SYSDATETIME()) > 30
--Does this clean anything up?
EXEC QSAutomation.QueryStore_CleanupUnusedPlans
--Anything left?
SELECT query_id, plan_id
FROM sys.query_store_plan
WHERE is_forced_plan = 1
AND DATEDIFF(day, last_execution_time, SYSDATETIME()) > 30
/**************************************************************************************************
Clean up
*************************************************************************************************/
DROP PROCEDURE QSAutomation.BadPlan
DROP PROCEDURE QSAutomation.ResetQueryStore
DROP TABLE QSAutomation.BigData