@@ -1368,6 +1368,20 @@ CREATE TABLE
13681368 max_query_wait_time_ms bigint NOT NULL
13691369);
13701370
1371+ /*
1372+ Tuning Recommendations, When Available (2017+)
1373+ */
1374+ CREATE TABLE
1375+ #tuning_recommendations
1376+ (
1377+ database_id integer NOT NULL ,
1378+ score integer NULL ,
1379+ last_refresh datetime2 NULL ,
1380+ /* These are from JSON, so the true schema cannot be known */
1381+ regressed_plan_id bigint NULL ,
1382+ recommended_plan_id bigint NULL
1383+ );
1384+
13711385/*
13721386Context is everything
13731387*/
@@ -1582,6 +1596,7 @@ VALUES
15821596 (90 , ' sql_2022' , ' plan_type' , ' plan_type_desc' , ' qsp.plan_type_desc' , 1 , ' sql_2022_views' , 1 , 0 , NULL ),
15831597 /* New version features */
15841598 (95 , ' new_features' , ' forcing_type' , ' plan_forcing_type_desc' , ' qsp.plan_forcing_type_desc' , 1 , ' new' , 1 , 0 , NULL ),
1599+ (96 , ' new_features' , ' forcing_status' , ' plan_force_recommendation_status' , ' tr.plan_force_recommendation_status' , 1 , ' new' , 1 , 1 , NULL ),
15851600 (97 , ' new_features' , ' top_waits' , ' top_waits' , ' w.top_waits' , 1 , ' new' , 1 , 0 , NULL ),
15861601 /* Date/time columns (not conditional, always included) */
15871602 (100 , ' execution_time' , ' first' , ' first_execution_time' , ' CASE WHEN @timezone IS NULL THEN SWITCHOFFSET(qsrs.first_execution_time, @utc_offset_string) WHEN @timezone IS NOT NULL THEN qsrs.first_execution_time AT TIME ZONE @timezone END' , 0 , NULL , NULL , 0 , NULL ),
@@ -8011,6 +8026,123 @@ OPTION(RECOMPILE);' + @nc10;
80118026 END ;
80128027END ; /* End getting wait stats*/
80138028
8029+ /*
8030+ If tuning recommendations are available, we'll grab them here
8031+ */
8032+ IF
8033+ (
8034+ @new = 1
8035+ AND @expert_mode = 1
8036+ )
8037+ BEGIN
8038+ SELECT
8039+ @current_table = ' inserting #tuning_recommendations' ,
8040+ @sql = @isolation_level;
8041+
8042+ IF @troubleshoot_performance = 1
8043+ BEGIN
8044+ EXECUTE sys .sp_executesql
8045+ @troubleshoot_insert,
8046+ N ' @current_table nvarchar(100)' ,
8047+ @current_table;
8048+
8049+ SET STATISTICS XML ON ;
8050+ END ;
8051+
8052+ /* The OPENJSON query doesn't work on pre-2017 compatibility levels. */
8053+ SELECT
8054+ @sql + = N'
8055+ SELECT
8056+ @database_id,
8057+ plan_force_flat.score,
8058+ plan_force_flat.last_refresh,
8059+ plan_force_flat.regressed_plan_id,
8060+ plan_force_flat.recommended_plan_id
8061+ FROM
8062+ (
8063+ SELECT
8064+ plan_force_json.score,
8065+ plan_force_json.last_refresh,
8066+ regressed_plan_id =
8067+ SUBSTRING
8068+ (
8069+ plan_force_json.Detail,
8070+ CHARINDEX('' regressedPlanId: '' , plan_force_json.detail) + LEN('' regressedPlanId: '' ),
8071+ CHARINDEX('' ,'' , plan_force_json.detail, CHARINDEX('' regressedPlanId: '' , plan_force_json.detail) + LEN('' ,'' ))
8072+ - LEN('' regressedPlanId: '' ) - CHARINDEX('' regressedPlanId: '' , plan_force_json.detail)
8073+ ),
8074+ recommended_plan_id =
8075+ SUBSTRING
8076+ (
8077+ plan_force_json.detail,
8078+ CHARINDEX('' recommendedPlanId: '' , plan_force_json.detail) + LEN('' recommendedPlanId: '' ),
8079+ CHARINDEX('' ,'' , plan_force_json.detail, CHARINDEX('' recommendedPlanId: '' , plan_force_json.detail) + LEN('' ,'' ))
8080+ - LEN('' recommendedPlanId: '' ) - CHARINDEX('' recommendedPlanId: '' , plan_force_json.detail)
8081+ )
8082+ FROM
8083+ (
8084+ SELECT
8085+ tr.score,
8086+ tr.last_refresh,
8087+ detail =
8088+ (
8089+ SELECT
8090+ REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
8091+ tr.details,
8092+ '' {'' , '' '' ), '' }'' , '' '' ), '' "'' , '' '' ), '' :'' , '' : '' ), '' planForceDetails: '' , '' '' ), '' ,'' , '' , '' )
8093+ )
8094+ FROM ' + @database_name_quoted + N' .sys.dm_db_tuning_recommendations AS tr
8095+ ) AS plan_force_json
8096+ ) AS plan_force_flat
8097+ WHERE EXISTS
8098+ (
8099+ SELECT
8100+ 1/0
8101+ FROM #query_store_plan AS qsp
8102+ WHERE plan_force_flat.regressed_plan_id = qsp.plan_id
8103+ )
8104+ OPTION(RECOMPILE);' + @nc10;
8105+
8106+ IF @debug = 1
8107+ BEGIN
8108+ PRINT LEN (@sql);
8109+ PRINT @sql;
8110+ END ;
8111+
8112+ INSERT
8113+ #tuning_recommendations
8114+ WITH
8115+ (TABLOCK )
8116+ (
8117+ database_id,
8118+ score,
8119+ last_refresh,
8120+ regressed_plan_id,
8121+ recommended_plan_id
8122+ )
8123+ EXECUTE sys .sp_executesql
8124+ @sql,
8125+ N ' @database_id integer' ,
8126+ @database_id;
8127+
8128+ IF @troubleshoot_performance = 1
8129+ BEGIN
8130+ SET STATISTICS XML OFF ;
8131+
8132+ EXECUTE sys .sp_executesql
8133+ @troubleshoot_update,
8134+ N ' @current_table nvarchar(100)' ,
8135+ @current_table;
8136+
8137+ EXECUTE sys .sp_executesql
8138+ @troubleshoot_info,
8139+ N' @sql nvarchar(max),
8140+ @current_table nvarchar(100)' ,
8141+ @sql,
8142+ @current_table;
8143+ END ;
8144+ END ; /* End getting tuning recommendations*/
8145+
80148146/*
80158147This gets context info and settings
80168148*/
@@ -8927,7 +9059,7 @@ SELECT
89279059 );
89289060
89299061 /*
8930- Get wait stats if we can
9062+ Get wait stats and tuning recommendations if we can
89319063 */
89329064 IF
89339065 (
@@ -8996,7 +9128,22 @@ SELECT
89969128 2,
89979129 '' ''
89989130 )
8999- ) AS w'
9131+ ) AS w
9132+ OUTER APPLY
9133+ (
9134+ SELECT TOP (1)
9135+ plan_force_recommendation_status =
9136+ N'' Considered regressed from plan_id '' +
9137+ CONVERT(nvarchar(20), qstr.recommended_plan_id) +
9138+ N'' . That plan may not be the best plan, but it is scored '' +
9139+ CONVERT(nvarchar(20), qstr.score) +
9140+ N'' out of 100 for being better than this plan.''
9141+ FROM #tuning_recommendations AS qstr
9142+ WHERE qstr.regressed_plan_id = qsrs.plan_id
9143+ AND qstr.database_id = qsrs.database_id
9144+ ORDER BY
9145+ qstr.last_refresh DESC
9146+ ) AS tr'
90009147 );
90019148 END ; /* End wait stats query*/
90029149
@@ -11601,6 +11748,29 @@ BEGIN
1160111748 END ;
1160211749 END ;
1160311750
11751+ IF EXISTS
11752+ (
11753+ SELECT
11754+ 1 / 0
11755+ FROM #tuning_recommendations AS qstr
11756+ )
11757+ BEGIN
11758+ SELECT
11759+ table_name =
11760+ ' #tuning_recommendations' ,
11761+ qstr.*
11762+ FROM #tuning_recommendations AS qstr
11763+ ORDER BY
11764+ qstr .regressed_plan_id
11765+ OPTION (RECOMPILE );
11766+ END ;
11767+ ELSE
11768+ BEGIN
11769+ SELECT
11770+ result =
11771+ ' #tuning_recommendations is empty' ;
11772+ END ;
11773+
1160411774 IF EXISTS
1160511775 (
1160611776 SELECT
0 commit comments