11/* *******************************************************
22* SETUP - clear everything
33********************************************************/
4- EXEC [dbo].[initialize]
5-
4+ ALTER DATABASE current SET AUTOMATIC_TUNING (FORCE_LAST_GOOD_PLAN = OFF );
5+ EXEC dbo . initialize ;
66
77/* *******************************************************
88* PART I
9- * Plan regression identification.
9+ * Plan regression identification & manual tuning
1010********************************************************/
1111
12- -- 1. Start workload - execute procedure 30-300 times:
13- begin
14- declare @packagetypeid int = 7 ;
15- exec dbo .report @packagetypeid
16- end
17- go 300
18- -- Queries should be fast
19- -- Optionally, include "Actual execution plan" in SSMS and show the plan (it should have Hash Aggregate)
12+ -- Execute the query and include "Actual execution plan" in SSMS and show the plan - it should have Hash Match (Aggregate) operator with Columnstore Index Scan
13+ EXEC sp_executesql N' select avg([UnitPrice]*[Quantity])
14+ from Sales.OrderLines
15+ where PackageTypeID = @packagetypeid' , N ' @packagetypeid int' , @packagetypeid = 7 ;
16+ GO 60
17+ -- 1. Execute this query 45-300 times to setup the baseline.
18+ -- If you have QUERY_STORE CAPTURE_POLICY=AUTO increase number in GO <number> to at least 60
2019
2120
22- -- 2. Execute procedure that causes plan regression
23- -- Optionally, include "Actual execution plan" in SSMS and show the plan ( it should have Stream Aggregate)
24- exec dbo .regression
21+ -- 2. Execute the procedure that causes plan regression
22+ -- Optionally, include "Actual execution plan" in SSMS and show the plan - it should have Stream Aggregate, Index Seek & Nested Loops
23+ EXEC dbo .regression ;
2524
2625
27- -- 3. Start workload again - verify that is slower.
28- begin
29- declare @packagetypeid int = 7 ;
30- exec dbo .report @packagetypeid
31- end
26+ -- 3. Start the workload again - verify that is slower.
27+ EXEC sp_executesql N' select avg([UnitPrice]*[Quantity])
28+ from Sales.OrderLines
29+ where PackageTypeID = @packagetypeid' , N ' @packagetypeid int' , @packagetypeid = 7 ;
3230go 20
33- -- Optionally, include "Actual execution plan" in SSMS and show the plan ( it should have Stream Aggregate)
31+ -- Optionally, include "Actual execution plan" in SSMS and show the plan - it should have Stream Aggregate with Non-clustered index seek.
3432
35- -- 4. Find recommendation recommended by database:
36- SELECT planForceDetails .query_id , reason, score,
37- JSON_VALUE(details, ' $.implementationDetails.script' ) [correction script],
38- planForceDetails.[new plan_id], planForceDetails.[recommended plan_id]
39- FROM sys .dm_db_tuning_recommendations
40- CROSS APPLY OPENJSON (Details, ' $.planForceDetails' )
41- WITH ( [query_id] int ' $.queryId' ,
42- [new plan_id] int ' $.regressedPlanId' ,
43- [recommended plan_id] int ' $.recommendedPlanId'
44- ) as planForceDetails;
33+ -- 4. Find a recommendation that can fix this issue:
34+ SELECT reason, score,
35+ script = JSON_VALUE(details, ' $.implementationDetails.script' )
36+ FROM sys .dm_db_tuning_recommendations ;
4537
38+ -- 4.1. Optionally get more detailed information about the regression and recommendation.
39+ SELECT reason, score,
40+ script = JSON_VALUE(details, ' $.implementationDetails.script' ),
41+ planForceDetails.[query_id],
42+ planForceDetails.[new plan_id],
43+ planForceDetails.[recommended plan_id],
44+ estimated_gain = (regressedPlanExecutionCount+ recommendedPlanExecutionCount)* (regressedPlanCpuTimeAverage- recommendedPlanCpuTimeAverage)/ 1000000 ,
45+ error_prone = IIF (regressedPlanErrorCount> recommendedPlanErrorCount, ' YES' ,' NO' )
46+ FROM sys .dm_db_tuning_recommendations
47+ CROSS APPLY OPENJSON (Details, ' $.planForceDetails' )
48+ WITH ( [query_id] int ' $.queryId' ,
49+ [new plan_id] int ' $.regressedPlanId' ,
50+ [recommended plan_id] int ' $.recommendedPlanId' ,
51+ regressedPlanErrorCount int ,
52+ recommendedPlanErrorCount int ,
53+ regressedPlanExecutionCount int ,
54+ regressedPlanCpuTimeAverage float ,
55+ recommendedPlanExecutionCount int ,
56+ recommendedPlanCpuTimeAverage float ) as planForceDetails;
57+ -- IMPORTANT NOTE: check is estimated_gain > 10.
58+ -- If estimated_gain < 10 THEN FLGP=ON will not automatically force the plan!!!
59+ -- In that case increase the number of executions in initial workload.
60+ -- Make sure that SQL Engine uses columnstore in original plan and nonclustered index in regressed plan.
4661
4762-- Note: User can apply script and force the recommended plan to correct the error.
4863<< Insert T- SQL from the script column here and execute the script>>
4964-- e.g.: exec sp_query_store_force_plan @query_id = 3, @plan_id = 1
5065
51- -- 5. Start workload again - verify that is faster.
52- begin
53- declare @packagetypeid int = 7 ;
54- exec dbo .report @packagetypeid
55- end
56- go 20
57- -- Optionally, include "Actual execution plan" in SSMS and show the plan (it should have Hash Aggregate again)
66+ -- 5. Execute the query again - verify that it is faster.
67+ EXEC sp_executesql N' select avg([UnitPrice]*[Quantity])
68+ from Sales.OrderLines
69+ where PackageTypeID = @packagetypeid' , N ' @packagetypeid int' , @packagetypeid = 7 ;
70+ GO 20
71+ -- Optionally, include "Actual execution plan" in SSMS and show the plan - it should have Hash Aggregate & Columnstore again
5872
5973
6074-- In part II will be shown better approach - automatic tuning.
@@ -74,29 +88,26 @@ ALTER DATABASE current
7488SET AUTOMATIC_TUNING ( FORCE_LAST_GOOD_PLAN = ON );
7589
7690-- Verify that actual state on FLGP is ON:
77- SELECT name , desired_state_desc, actual_state_desc, reason_desc
78- FROM sys .database_automatic_tuning_options ;
79-
91+ SELECT name , actual_state_desc, status = IIF (desired_state_desc <> actual_state_desc, reason_desc, ' Status:OK' )
92+ FROM sys .database_automatic_tuning_options
93+ WHERE name = ' FORCE_LAST_GOOD_PLAN' ;
8094
8195-- 1. Start workload - execute procedure 30-300 times like in the phase I
82- begin
83- declare @packagetypeid int = 7 ;
84- exec dbo .report @packagetypeid
85- end
86- go 300
96+ EXEC sp_executesql N' select avg([UnitPrice]*[Quantity])
97+ from Sales.OrderLines
98+ where PackageTypeID = @packagetypeid' , N ' @packagetypeid int' , @packagetypeid = 7 ;
99+ GO 60
87100
88- -- 2. Execute the procedure that causes plan regression
101+ -- 2. Execute the procedure that causes the plan regression
89102exec dbo .regression ;
90103
91- -- 3. Start workload again - verify that it is slower.
92- begin
93- declare @packagetypeid int = 7 ;
94- exec dbo .report @packagetypeid;
95- end
96- go 20
104+ -- 3. Start the workload again - verify that it is slower.
105+ EXEC sp_executesql N' select avg([UnitPrice]*[Quantity])
106+ from Sales.OrderLines
107+ where PackageTypeID = @packagetypeid' , N ' @packagetypeid int' , @packagetypeid = 7 ;
108+ go 30
97109
98- -- 4. Find recommendation that returns query perf regression
99- -- and check is it in Verifying state:
110+ -- 4. Find a recommendation and check is it in "Verifying" or "Success" state:
100111SELECT reason, score,
101112 JSON_VALUE(state , ' $.currentValue' ) state ,
102113 JSON_VALUE(state , ' $.reason' ) state_transition_reason,
@@ -110,11 +121,11 @@ FROM sys.dm_db_tuning_recommendations
110121 ) as planForceDetails;
111122
112123
113- -- 5. Wait until recommendation is applied and start workload again - verify that it is faster.
114- begin
115- declare @packagetypeid int = 7 ;
116- exec dbo .report @packagetypeid
117- end
118- go 30
124+ -- 5. Recommendation is in "Verifying" state, but the last good plan is forced, so the query will be faster:
125+ EXEC sp_executesql N' select avg([UnitPrice]*[Quantity])
126+ from Sales.OrderLines
127+ where PackageTypeID = @packagetypeid' , N ' @packagetypeid int' , @packagetypeid = 7 ;
128+
129+
130+ -- Open Query Store/"Top Resource Consuming Queries" dialog in SSMS and show that the better plan is forced.
119131
120- -- Open Query Store/"Top Resource Consuming Queries" dialog in SSMS and show that better plan is forced.
0 commit comments