@@ -614,6 +614,7 @@ CREATE TABLE #working_warnings
614
614
is_paul_white_electric BIT ,
615
615
implicit_conversion_info XML ,
616
616
cached_execution_parameters XML ,
617
+ missing_indexes XML ,
617
618
warnings NVARCHAR (4000 )
618
619
INDEX ww_ix_ids CLUSTERED (plan_id, query_id, query_hash, sql_handle )
619
620
);
@@ -817,8 +818,120 @@ CREATE TABLE #conversion_info
817
818
INDEX cif_ix_ids CLUSTERED (sql_handle , query_hash)
818
819
);
819
820
821
+ /* These tables support the Missing Index details clickable*/
820
822
821
823
824
+ DROP TABLE IF EXISTS #missing_index_xml
825
+
826
+ CREATE TABLE #missing_index_xml
827
+ (
828
+ query_hash BINARY (8 ),
829
+ sql_handle VARBINARY (64 ),
830
+ impact FLOAT ,
831
+ index_xml XML
832
+ );
833
+
834
+ DROP TABLE IF EXISTS #missing_index_schema
835
+
836
+ CREATE TABLE #missing_index_schema
837
+ (
838
+ query_hash BINARY (8 ),
839
+ sql_handle VARBINARY (64 ),
840
+ impact FLOAT ,
841
+ database_name NVARCHAR (128 ),
842
+ schema_name NVARCHAR (128 ),
843
+ table_name NVARCHAR (128 ),
844
+ index_xml XML
845
+ );
846
+
847
+
848
+ DROP TABLE IF EXISTS #missing_index_usage
849
+
850
+ CREATE TABLE #missing_index_usage
851
+ (
852
+ query_hash BINARY (8 ),
853
+ sql_handle VARBINARY (64 ),
854
+ impact FLOAT ,
855
+ database_name NVARCHAR (128 ),
856
+ schema_name NVARCHAR (128 ),
857
+ table_name NVARCHAR (128 ),
858
+ usage NVARCHAR (128 ),
859
+ index_xml XML
860
+ );
861
+
862
+ DROP TABLE IF EXISTS #missing_index_detail
863
+
864
+ CREATE TABLE #missing_index_detail
865
+ (
866
+ query_hash BINARY (8 ),
867
+ sql_handle VARBINARY (64 ),
868
+ impact FLOAT ,
869
+ database_name NVARCHAR (128 ),
870
+ schema_name NVARCHAR (128 ),
871
+ table_name NVARCHAR (128 ),
872
+ usage NVARCHAR (128 ),
873
+ column_name NVARCHAR (128 )
874
+ );
875
+
876
+
877
+ DROP TABLE IF EXISTS #missing_index_pretty
878
+
879
+ CREATE TABLE #missing_index_pretty
880
+ (
881
+ query_hash BINARY (8 ),
882
+ sql_handle VARBINARY (64 ),
883
+ impact FLOAT ,
884
+ database_name NVARCHAR (128 ),
885
+ schema_name NVARCHAR (128 ),
886
+ table_name NVARCHAR (128 ),
887
+ equality NVARCHAR (4000 ),
888
+ inequality NVARCHAR (4000 ),
889
+ [include] NVARCHAR (4000 ),
890
+ details AS N ' /* '
891
+ + CHAR (10 )
892
+ + N ' The Query Processor estimates that implementing the following index could improve the query cost by '
893
+ + CONVERT (NVARCHAR (30 ), impact)
894
+ + ' %.'
895
+ + CHAR (10 )
896
+ + N ' */'
897
+ + CHAR (10 ) + CHAR (13 )
898
+ + N ' /* '
899
+ + CHAR (10 )
900
+ + N ' USE '
901
+ + database_name
902
+ + CHAR (10 )
903
+ + N ' GO'
904
+ + CHAR (10 ) + CHAR (13 )
905
+ + N ' CREATE NONCLUSTERED INDEX ix_'
906
+ + ISNULL (REPLACE (REPLACE (REPLACE (equality,' [' , ' ' ), ' ]' , ' ' ), ' , ' , ' _' ), ' ' )
907
+ + ISNULL (REPLACE (REPLACE (REPLACE (inequality,' [' , ' ' ), ' ]' , ' ' ), ' , ' , ' _' ), ' ' )
908
+ + CASE WHEN [include] IS NOT NULL THEN + N ' Includes' ELSE N ' ' END
909
+ + CHAR (10 )
910
+ + N ' ON '
911
+ + schema_name
912
+ + N ' .'
913
+ + table_name
914
+ + N ' (' +
915
+ + CASE WHEN equality IS NOT NULL
916
+ THEN equality
917
+ + CASE WHEN inequality IS NOT NULL
918
+ THEN N ' , ' + inequality
919
+ ELSE N ' '
920
+ END
921
+ ELSE inequality
922
+ END
923
+ + N ' )'
924
+ + CHAR (10 )
925
+ + CASE WHEN include IS NOT NULL
926
+ THEN N ' INCLUDE (' + include + N ' )'
927
+ ELSE N ' '
928
+ END
929
+ + CHAR (10 )
930
+ + N ' GO'
931
+ + CHAR (10 )
932
+ + N ' */'
933
+ );
934
+
822
935
/* Sets up WHERE clause that gets used quite a bit*/
823
936
824
937
-- Date stuff
@@ -3077,7 +3190,141 @@ SET b.implicit_conversion_info = CASE WHEN b.implicit_conversion_info IS NULL
3077
3190
FROM #working_warnings AS b
3078
3191
OPTION ( RECOMPILE );
3079
3192
3193
+ /* Begin Missing Index*/
3080
3194
3195
+ IF EXISTS
3196
+ (SELECT 1 FROM #working_warnings AS ww WHERE ww .missing_index_count > 0 )
3197
+
3198
+ BEGIN
3199
+
3200
+ RAISERROR (N ' Inserting to #missing_index_xml' , 0 , 1 ) WITH NOWAIT ;
3201
+ WITH XMLNAMESPACES ( ' http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3202
+ INSERT #missing_index_xml
3203
+ SELECT qp .query_hash ,
3204
+ qp .sql_handle ,
3205
+ c .mg .value (' @Impact' , ' FLOAT' ) AS Impact,
3206
+ c .mg .query(' .' ) AS cmg
3207
+ FROM #query_plan AS qp
3208
+ CROSS APPLY qp .query_plan .nodes (' //p:MissingIndexes/p:MissingIndexGroup' ) AS c(mg)
3209
+ WHERE qp .query_hash IS NOT NULL
3210
+ AND c .mg .value (' @Impact' , ' FLOAT' ) > 70 .0
3211
+ OPTION (RECOMPILE );
3212
+
3213
+ RAISERROR (N ' Inserting to #missing_index_schema' , 0 , 1 ) WITH NOWAIT ;
3214
+ WITH XMLNAMESPACES ( ' http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3215
+ INSERT #missing_index_schema
3216
+ SELECT mix .query_hash , mix .sql_handle , mix .impact ,
3217
+ c .mi .value (' @Database' , ' NVARCHAR(128)' ) ,
3218
+ c .mi .value (' @Schema' , ' NVARCHAR(128)' ) ,
3219
+ c .mi .value (' @Table' , ' NVARCHAR(128)' ) ,
3220
+ c .mi .query(' .' )
3221
+ FROM #missing_index_xml AS mix
3222
+ CROSS APPLY mix .index_xml .nodes (' //p:MissingIndex' ) AS c(mi)
3223
+ OPTION (RECOMPILE );
3224
+
3225
+ RAISERROR (N ' Inserting to #missing_index_usage' , 0 , 1 ) WITH NOWAIT ;
3226
+ WITH XMLNAMESPACES ( ' http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3227
+ INSERT #missing_index_usage
3228
+ SELECT ms .query_hash , ms .sql_handle , ms .impact , ms .database_name , ms .schema_name , ms .table_name ,
3229
+ c .cg .value (' @Usage' , ' NVARCHAR(128)' ),
3230
+ c .cg .query(' .' )
3231
+ FROM #missing_index_schema ms
3232
+ CROSS APPLY ms .index_xml .nodes (' //p:ColumnGroup' ) AS c(cg)
3233
+ OPTION (RECOMPILE );
3234
+
3235
+ RAISERROR (N ' Inserting to #missing_index_detail' , 0 , 1 ) WITH NOWAIT ;
3236
+ WITH XMLNAMESPACES ( ' http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3237
+ INSERT #missing_index_detail
3238
+ SELECT miu .query_hash ,
3239
+ miu .sql_handle ,
3240
+ miu .impact ,
3241
+ miu .database_name ,
3242
+ miu .schema_name ,
3243
+ miu .table_name ,
3244
+ miu .usage ,
3245
+ c .c .value (' @Name' , ' NVARCHAR(128)' )
3246
+ FROM #missing_index_usage AS miu
3247
+ CROSS APPLY miu .index_xml .nodes (' //p:Column' ) AS c(c)
3248
+ OPTION (RECOMPILE );
3249
+
3250
+ RAISERROR (N ' Inserting to #missing_index_pretty' , 0 , 1 ) WITH NOWAIT ;
3251
+ INSERT #missing_index_pretty
3252
+ SELECT m .query_hash , m .sql_handle , m .impact , m .database_name , m .schema_name , m .table_name
3253
+ , STUFF (( SELECT DISTINCT N ' , ' + ISNULL (m2 .column_name , ' ' ) AS column_name
3254
+ FROM #missing_index_detail AS m2
3255
+ WHERE m2 .usage = ' EQUALITY'
3256
+ AND m .query_hash = m2 .query_hash
3257
+ AND m .sql_handle = m2 .sql_handle
3258
+ AND m .impact = m2 .impact
3259
+ AND m .database_name = m2 .database_name
3260
+ AND m .schema_name = m2 .schema_name
3261
+ AND m .table_name = m2 .table_name
3262
+ FOR XML PATH (N ' ' ), TYPE ).value (N ' .[1]' , N ' NVARCHAR(4000)' ), 1 , 2 , N ' ' ) AS equality
3263
+ , STUFF (( SELECT DISTINCT N ' , ' + ISNULL (m2 .column_name , ' ' ) AS column_name
3264
+ FROM #missing_index_detail AS m2
3265
+ WHERE m2 .usage = ' INEQUALITY'
3266
+ AND m .query_hash = m2 .query_hash
3267
+ AND m .sql_handle = m2 .sql_handle
3268
+ AND m .impact = m2 .impact
3269
+ AND m .database_name = m2 .database_name
3270
+ AND m .schema_name = m2 .schema_name
3271
+ AND m .table_name = m2 .table_name
3272
+ FOR XML PATH (N ' ' ), TYPE ).value (N ' .[1]' , N ' NVARCHAR(4000)' ), 1 , 2 , N ' ' ) AS inequality
3273
+ , STUFF (( SELECT DISTINCT N ' , ' + ISNULL (m2 .column_name , ' ' ) AS column_name
3274
+ FROM #missing_index_detail AS m2
3275
+ WHERE m2 .usage = ' INCLUDE'
3276
+ AND m .query_hash = m2 .query_hash
3277
+ AND m .sql_handle = m2 .sql_handle
3278
+ AND m .impact = m2 .impact
3279
+ AND m .database_name = m2 .database_name
3280
+ AND m .schema_name = m2 .schema_name
3281
+ AND m .table_name = m2 .table_name
3282
+ FOR XML PATH (N ' ' ), TYPE ).value (N ' .[1]' , N ' NVARCHAR(4000)' ), 1 , 2 , N ' ' ) AS [include]
3283
+ FROM #missing_index_detail AS m
3284
+ GROUP BY m .query_hash , m .sql_handle , m .impact , m .database_name , m .schema_name , m .table_name
3285
+ OPTION (RECOMPILE );
3286
+
3287
+ RAISERROR (N ' Updating missing index information' , 0 , 1 ) WITH NOWAIT ;
3288
+ WITH missing AS (
3289
+ SELECT mip .query_hash ,
3290
+ mip .sql_handle ,
3291
+ CONVERT (XML ,
3292
+ N ' <?MissingIndexes -- '
3293
+ + CHAR (10 ) + CHAR (13 )
3294
+ + STUFF (( SELECT CHAR (10 ) + CHAR (13 ) + ISNULL (mip2 .details , ' ' ) AS details
3295
+ FROM #missing_index_pretty AS mip2
3296
+ WHERE mip .query_hash = mip2 .query_hash
3297
+ AND mip .sql_handle = mip2 .sql_handle
3298
+ GROUP BY mip2 .details
3299
+ ORDER BY MAX (mip2 .impact ) DESC
3300
+ FOR XML PATH (N ' ' ), TYPE ).value (N ' .[1]' , N ' NVARCHAR(4000)' ), 1 , 2 , N ' ' )
3301
+ + CHAR (10 ) + CHAR (13 )
3302
+ + N ' -- ?>'
3303
+ ) AS full_details
3304
+ FROM #missing_index_pretty AS mip
3305
+ GROUP BY mip .query_hash , mip .sql_handle , mip .impact
3306
+ )
3307
+ UPDATE ww
3308
+ SET ww .missing_indexes = m .full_details
3309
+ FROM #working_warnings AS ww
3310
+ JOIN missing AS m
3311
+ ON m .sql_handle = ww .sql_handle
3312
+ OPTION (RECOMPILE );
3313
+
3314
+
3315
+ END
3316
+
3317
+ RAISERROR (N ' Filling in missing index blanks' , 0 , 1 ) WITH NOWAIT ;
3318
+ UPDATE ww
3319
+ SET ww .missing_indexes =
3320
+ CASE WHEN ww .missing_indexes IS NULL
3321
+ THEN ' <?NoNeedToClickMe -- N/A --?>'
3322
+ ELSE ww .missing_indexes
3323
+ END
3324
+ FROM #working_warnings AS ww
3325
+ OPTION (RECOMPILE );
3326
+
3327
+ /* End Missing Index*/
3081
3328
3082
3329
RAISERROR (N ' General query dispositions: frequent executions, long running, etc.' , 0 , 1 ) WITH NOWAIT ;
3083
3330
@@ -3292,7 +3539,7 @@ RAISERROR(N'Returning regular results', 0, 1) WITH NOWAIT;
3292
3539
3293
3540
WITH x AS (
3294
3541
SELECT wpt .database_name , ww .query_cost , wm .plan_id , wm .query_id , wpt .query_sql_text , wm .proc_or_function_name , wpt .query_plan_xml , ww .warnings , wpt .pattern ,
3295
- wm .parameter_sniffing_symptoms , wpt .top_three_waits , ww .implicit_conversion_info , ww .cached_execution_parameters , wm .count_executions , wm .count_compiles , wm .total_cpu_time , wm .avg_cpu_time ,
3542
+ wm .parameter_sniffing_symptoms , wpt .top_three_waits , ww .missing_indexes , ww . implicit_conversion_info , ww .cached_execution_parameters , wm .count_executions , wm .count_compiles , wm .total_cpu_time , wm .avg_cpu_time ,
3296
3543
wm .total_duration , wm .avg_duration , wm .total_logical_io_reads , wm .avg_logical_io_reads ,
3297
3544
wm .total_physical_io_reads , wm .avg_physical_io_reads , wm .total_logical_io_writes , wm .avg_logical_io_writes , wm .total_rowcount , wm .avg_rowcount ,
3298
3545
wm .total_query_max_used_memory , wm .avg_query_max_used_memory , wm .total_tempdb_space_used , wm .avg_tempdb_space_used ,
@@ -3321,7 +3568,7 @@ RAISERROR(N'Returning results for failed queries', 0, 1) WITH NOWAIT;
3321
3568
3322
3569
WITH x AS (
3323
3570
SELECT wpt .database_name , ww .query_cost , wm .plan_id , wm .query_id , wpt .query_sql_text , wm .proc_or_function_name , wpt .query_plan_xml , ww .warnings , wpt .pattern ,
3324
- wm .parameter_sniffing_symptoms , wpt .last_force_failure_reason_desc , wpt .top_three_waits , ww .implicit_conversion_info , ww .cached_execution_parameters ,
3571
+ wm .parameter_sniffing_symptoms , wpt .last_force_failure_reason_desc , wpt .top_three_waits , ww .missing_indexes , ww . implicit_conversion_info , ww .cached_execution_parameters ,
3325
3572
wm .count_executions , wm .count_compiles , wm .total_cpu_time , wm .avg_cpu_time ,
3326
3573
wm .total_duration , wm .avg_duration , wm .total_logical_io_reads , wm .avg_logical_io_reads ,
3327
3574
wm .total_physical_io_reads , wm .avg_physical_io_reads , wm .total_logical_io_writes , wm .avg_logical_io_writes , wm .total_rowcount , wm .avg_rowcount ,
@@ -4428,6 +4675,26 @@ SELECT '#variable_info' AS table_name, *
4428
4675
FROM #variable_info AS vi
4429
4676
OPTION ( RECOMPILE );
4430
4677
4678
+ SELECT ' #missing_index_xml' AS table_name, *
4679
+ FROM #missing_index_xml
4680
+ OPTION ( RECOMPILE );
4681
+
4682
+ SELECT ' #missing_index_schema' AS table_name, *
4683
+ FROM #missing_index_schema
4684
+ OPTION ( RECOMPILE );
4685
+
4686
+ SELECT ' #missing_index_usage' AS table_name, *
4687
+ FROM #missing_index_usage
4688
+ OPTION ( RECOMPILE );
4689
+
4690
+ SELECT ' #missing_index_detail' AS table_name, *
4691
+ FROM #missing_index_detail
4692
+ OPTION ( RECOMPILE );
4693
+
4694
+ SELECT ' #missing_index_pretty' AS table_name, *
4695
+ FROM #missing_index_pretty
4696
+ OPTION ( RECOMPILE );
4697
+
4431
4698
END ;
4432
4699
4433
4700
END TRY
0 commit comments