· 6 years ago · Apr 17, 2019, 05:10 PM
1SET ANSI_NULLS ON;
2SET ANSI_PADDING ON;
3SET ANSI_WARNINGS ON;
4SET ARITHABORT ON;
5SET CONCAT_NULL_YIELDS_NULL ON;
6SET QUOTED_IDENTIFIER ON;
7SET STATISTICS IO OFF;
8SET STATISTICS TIME OFF;
9GO
10
11IF (
12SELECT
13 CASE
14 WHEN CONVERT(NVARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '8%' THEN 0
15 WHEN CONVERT(NVARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '9%' THEN 0
16 ELSE 1
17 END
18) = 0
19BEGIN
20 DECLARE @msg VARCHAR(8000);
21 SELECT @msg = 'Sorry, sp_BlitzCache doesn''t work on versions of SQL prior to 2008.' + REPLICATE(CHAR(13), 7933);
22 PRINT @msg;
23 RETURN;
24END;
25
26IF OBJECT_ID('dbo.sp_BlitzCache') IS NULL
27 EXEC ('CREATE PROCEDURE dbo.sp_BlitzCache AS RETURN 0;');
28GO
29
30IF OBJECT_ID('dbo.sp_BlitzCache') IS NOT NULL AND OBJECT_ID('tempdb.dbo.##BlitzCacheProcs', 'U') IS NOT NULL
31 EXEC ('DROP TABLE ##BlitzCacheProcs;');
32GO
33
34IF OBJECT_ID('dbo.sp_BlitzCache') IS NOT NULL AND OBJECT_ID('tempdb.dbo.##BlitzCacheResults', 'U') IS NOT NULL
35 EXEC ('DROP TABLE ##BlitzCacheResults;');
36GO
37
38CREATE TABLE ##BlitzCacheResults (
39 SPID INT,
40 ID INT IDENTITY(1,1),
41 CheckID INT,
42 Priority TINYINT,
43 FindingsGroup VARCHAR(50),
44 Finding VARCHAR(200),
45 URL VARCHAR(200),
46 Details VARCHAR(4000)
47);
48
49CREATE TABLE ##BlitzCacheProcs (
50 SPID INT ,
51 QueryType NVARCHAR(258),
52 DatabaseName sysname,
53 AverageCPU DECIMAL(38,4),
54 AverageCPUPerMinute DECIMAL(38,4),
55 TotalCPU DECIMAL(38,4),
56 PercentCPUByType MONEY,
57 PercentCPU MONEY,
58 AverageDuration DECIMAL(38,4),
59 TotalDuration DECIMAL(38,4),
60 PercentDuration MONEY,
61 PercentDurationByType MONEY,
62 AverageReads BIGINT,
63 TotalReads BIGINT,
64 PercentReads MONEY,
65 PercentReadsByType MONEY,
66 ExecutionCount BIGINT,
67 PercentExecutions MONEY,
68 PercentExecutionsByType MONEY,
69 ExecutionsPerMinute MONEY,
70 TotalWrites BIGINT,
71 AverageWrites MONEY,
72 PercentWrites MONEY,
73 PercentWritesByType MONEY,
74 WritesPerMinute MONEY,
75 PlanCreationTime DATETIME,
76 PlanCreationTimeHours AS DATEDIFF(HOUR, PlanCreationTime, SYSDATETIME()),
77 LastExecutionTime DATETIME,
78 PlanHandle VARBINARY(64),
79 [Remove Plan Handle From Cache] AS
80 CASE WHEN [PlanHandle] IS NOT NULL
81 THEN 'DBCC FREEPROCCACHE (' + CONVERT(VARCHAR(128), [PlanHandle], 1) + ');'
82 ELSE 'N/A' END,
83 SqlHandle VARBINARY(64),
84 [Remove SQL Handle From Cache] AS
85 CASE WHEN [SqlHandle] IS NOT NULL
86 THEN 'DBCC FREEPROCCACHE (' + CONVERT(VARCHAR(128), [SqlHandle], 1) + ');'
87 ELSE 'N/A' END,
88 [SQL Handle More Info] AS
89 CASE WHEN [SqlHandle] IS NOT NULL
90 THEN 'EXEC sp_BlitzCache @OnlySqlHandles = ''' + CONVERT(VARCHAR(128), [SqlHandle], 1) + '''; '
91 ELSE 'N/A' END,
92 QueryHash BINARY(8),
93 [Query Hash More Info] AS
94 CASE WHEN [QueryHash] IS NOT NULL
95 THEN 'EXEC sp_BlitzCache @OnlyQueryHashes = ''' + CONVERT(VARCHAR(32), [QueryHash], 1) + '''; '
96 ELSE 'N/A' END,
97 QueryPlanHash BINARY(8),
98 StatementStartOffset INT,
99 StatementEndOffset INT,
100 MinReturnedRows BIGINT,
101 MaxReturnedRows BIGINT,
102 AverageReturnedRows MONEY,
103 TotalReturnedRows BIGINT,
104 LastReturnedRows BIGINT,
105 /*The Memory Grant columns are only supported
106 in certain versions, giggle giggle.
107 */
108 MinGrantKB BIGINT,
109 MaxGrantKB BIGINT,
110 MinUsedGrantKB BIGINT,
111 MaxUsedGrantKB BIGINT,
112 PercentMemoryGrantUsed MONEY,
113 AvgMaxMemoryGrant MONEY,
114 MinSpills BIGINT,
115 MaxSpills BIGINT,
116 TotalSpills BIGINT,
117 AvgSpills MONEY,
118 QueryText NVARCHAR(MAX),
119 QueryPlan XML,
120 /* these next four columns are the total for the type of query.
121 don't actually use them for anything apart from math by type.
122 */
123 TotalWorkerTimeForType BIGINT,
124 TotalElapsedTimeForType BIGINT,
125 TotalReadsForType BIGINT,
126 TotalExecutionCountForType BIGINT,
127 TotalWritesForType BIGINT,
128 NumberOfPlans INT,
129 NumberOfDistinctPlans INT,
130 SerialDesiredMemory FLOAT,
131 SerialRequiredMemory FLOAT,
132 CachedPlanSize FLOAT,
133 CompileTime FLOAT,
134 CompileCPU FLOAT ,
135 CompileMemory FLOAT ,
136 MaxCompileMemory FLOAT ,
137 min_worker_time BIGINT,
138 max_worker_time BIGINT,
139 is_forced_plan BIT,
140 is_forced_parameterized BIT,
141 is_cursor BIT,
142 is_optimistic_cursor BIT,
143 is_forward_only_cursor BIT,
144 is_fast_forward_cursor BIT,
145 is_cursor_dynamic BIT,
146 is_parallel BIT,
147 is_forced_serial BIT,
148 is_key_lookup_expensive BIT,
149 key_lookup_cost FLOAT,
150 is_remote_query_expensive BIT,
151 remote_query_cost FLOAT,
152 frequent_execution BIT,
153 parameter_sniffing BIT,
154 unparameterized_query BIT,
155 near_parallel BIT,
156 plan_warnings BIT,
157 plan_multiple_plans BIT,
158 long_running BIT,
159 downlevel_estimator BIT,
160 implicit_conversions BIT,
161 busy_loops BIT,
162 tvf_join BIT,
163 tvf_estimate BIT,
164 compile_timeout BIT,
165 compile_memory_limit_exceeded BIT,
166 warning_no_join_predicate BIT,
167 QueryPlanCost FLOAT,
168 missing_index_count INT,
169 unmatched_index_count INT,
170 min_elapsed_time BIGINT,
171 max_elapsed_time BIGINT,
172 age_minutes MONEY,
173 age_minutes_lifetime MONEY,
174 is_trivial BIT,
175 trace_flags_session VARCHAR(1000),
176 is_unused_grant BIT,
177 function_count INT,
178 clr_function_count INT,
179 is_table_variable BIT,
180 no_stats_warning BIT,
181 relop_warnings BIT,
182 is_table_scan BIT,
183 backwards_scan BIT,
184 forced_index BIT,
185 forced_seek BIT,
186 forced_scan BIT,
187 columnstore_row_mode BIT,
188 is_computed_scalar BIT ,
189 is_sort_expensive BIT,
190 sort_cost FLOAT,
191 is_computed_filter BIT,
192 op_name VARCHAR(100) NULL,
193 index_insert_count INT NULL,
194 index_update_count INT NULL,
195 index_delete_count INT NULL,
196 cx_insert_count INT NULL,
197 cx_update_count INT NULL,
198 cx_delete_count INT NULL,
199 table_insert_count INT NULL,
200 table_update_count INT NULL,
201 table_delete_count INT NULL,
202 index_ops AS (index_insert_count + index_update_count + index_delete_count +
203 cx_insert_count + cx_update_count + cx_delete_count +
204 table_insert_count + table_update_count + table_delete_count),
205 is_row_level BIT,
206 is_spatial BIT,
207 index_dml BIT,
208 table_dml BIT,
209 long_running_low_cpu BIT,
210 low_cost_high_cpu BIT,
211 stale_stats BIT,
212 is_adaptive BIT,
213 index_spool_cost FLOAT,
214 index_spool_rows FLOAT,
215 is_spool_expensive BIT,
216 is_spool_more_rows BIT,
217 estimated_rows FLOAT,
218 is_bad_estimate BIT,
219 is_paul_white_electric BIT,
220 is_row_goal BIT,
221 is_big_spills BIT,
222 is_mstvf BIT,
223 is_mm_join BIT,
224 is_nonsargable BIT,
225 implicit_conversion_info XML,
226 cached_execution_parameters XML,
227 missing_indexes XML,
228 SetOptions VARCHAR(MAX),
229 Warnings VARCHAR(MAX)
230 );
231GO
232
233ALTER PROCEDURE dbo.sp_BlitzCache
234 @Help BIT = 0,
235 @Top INT = NULL,
236 @SortOrder VARCHAR(50) = 'CPU',
237 @UseTriggersAnyway BIT = NULL,
238 @ExportToExcel BIT = 0,
239 @ExpertMode TINYINT = 0,
240 @OutputServerName NVARCHAR(258) = NULL ,
241 @OutputDatabaseName NVARCHAR(258) = NULL ,
242 @OutputSchemaName NVARCHAR(258) = NULL ,
243 @OutputTableName NVARCHAR(258) = NULL ,
244 @ConfigurationDatabaseName NVARCHAR(128) = NULL ,
245 @ConfigurationSchemaName NVARCHAR(258) = NULL ,
246 @ConfigurationTableName NVARCHAR(258) = NULL ,
247 @DurationFilter DECIMAL(38,4) = NULL ,
248 @HideSummary BIT = 0 ,
249 @IgnoreSystemDBs BIT = 1 ,
250 @OnlyQueryHashes VARCHAR(MAX) = NULL ,
251 @IgnoreQueryHashes VARCHAR(MAX) = NULL ,
252 @OnlySqlHandles VARCHAR(MAX) = NULL ,
253 @IgnoreSqlHandles VARCHAR(MAX) = NULL ,
254 @QueryFilter VARCHAR(10) = 'ALL' ,
255 @DatabaseName NVARCHAR(128) = NULL ,
256 @StoredProcName NVARCHAR(128) = NULL,
257 @SlowlySearchPlansFor NVARCHAR(4000) = NULL,
258 @Reanalyze BIT = 0 ,
259 @SkipAnalysis BIT = 0 ,
260 @BringThePain BIT = 0, /* This will forcibly set @Top to 2,147,483,647 */
261 @MinimumExecutionCount INT = 0,
262 @Debug BIT = 0,
263 @CheckDateOverride DATETIMEOFFSET = NULL,
264 @MinutesBack INT = NULL,
265 @Version VARCHAR(30) = NULL OUTPUT,
266 @VersionDate DATETIME = NULL OUTPUT,
267 @VersionCheckMode BIT = 0
268WITH RECOMPILE
269AS
270BEGIN
271SET NOCOUNT ON;
272SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
273
274SELECT @Version = '7.4', @VersionDate = '20190320';
275
276
277IF(@VersionCheckMode = 1)
278BEGIN
279 RETURN;
280END;
281
282IF @Help = 1 PRINT '
283sp_BlitzCache from http://FirstResponderKit.org
284
285This script displays your most resource-intensive queries from the plan cache,
286and points to ways you can tune these queries to make them faster.
287
288
289To learn more, visit http://FirstResponderKit.org where you can download new
290versions for free, watch training videos on how it works, get more info on
291the findings, contribute your own code, and more.
292
293Known limitations of this version:
294 - This query will not run on SQL Server 2005.
295 - SQL Server 2008 and 2008R2 have a bug in trigger stats, so that output is
296 excluded by default.
297 - @IgnoreQueryHashes and @OnlyQueryHashes require a CSV list of hashes
298 with no spaces between the hash values.
299 - @OutputServerName is not functional yet.
300
301Unknown limitations of this version:
302 - May or may not be vulnerable to the wick effect.
303
304Changes - for the full list of improvements and fixes in this version, see:
305https://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit/
306
307
308
309MIT License
310
311Copyright (c) 2019 Brent Ozar Unlimited
312
313Permission is hereby granted, free of charge, to any person obtaining a copy
314of this software and associated documentation files (the "Software"), to deal
315in the Software without restriction, including without limitation the rights
316to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
317copies of the Software, and to permit persons to whom the Software is
318furnished to do so, subject to the following conditions:
319
320The above copyright notice and this permission notice shall be included in all
321copies or substantial portions of the Software.
322
323THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
324IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
325FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
326AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
327LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
328OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
329SOFTWARE.
330';
331
332DECLARE @nl NVARCHAR(2) = NCHAR(13) + NCHAR(10) ;
333
334IF @Help = 1
335BEGIN
336 SELECT N'@Help' AS [Parameter Name] ,
337 N'BIT' AS [Data Type] ,
338 N'Displays this help message.' AS [Parameter Description]
339
340 UNION ALL
341 SELECT N'@Top',
342 N'INT',
343 N'The number of records to retrieve and analyze from the plan cache. The following DMVs are used as the plan cache: dm_exec_query_stats, dm_exec_procedure_stats, dm_exec_trigger_stats.'
344
345 UNION ALL
346 SELECT N'@SortOrder',
347 N'VARCHAR(10)',
348 N'Data processing and display order. @SortOrder will still be used, even when preparing output for a table or for excel. Possible values are: "CPU", "Reads", "Writes", "Duration", "Executions", "Recent Compilations", "Memory Grant", "Spills". Additionally, the word "Average" or "Avg" can be used to sort on averages rather than total. "Executions per minute" and "Executions / minute" can be used to sort by execution per minute. For the truly lazy, "xpm" can also be used. Note that when you use all or all avg, the only parameters you can use are @Top and @DatabaseName. All others will be ignored.'
349
350 UNION ALL
351 SELECT N'@UseTriggersAnyway',
352 N'BIT',
353 N'On SQL Server 2008R2 and earlier, trigger execution count is incorrect - trigger execution count is incremented once per execution of a SQL agent job. If you still want to see relative execution count of triggers, then you can force sp_BlitzCache to include this information.'
354
355 UNION ALL
356 SELECT N'@ExportToExcel',
357 N'BIT',
358 N'Prepare output for exporting to Excel. Newlines and additional whitespace are removed from query text and the execution plan is not displayed.'
359
360 UNION ALL
361 SELECT N'@ExpertMode',
362 N'TINYINT',
363 N'Default 0. When set to 1, results include more columns. When 2, mode is optimized for Opserver, the open source dashboard.'
364
365 UNION ALL
366 SELECT N'@OutputDatabaseName',
367 N'NVARCHAR(128)',
368 N'The output database. If this does not exist SQL Server will divide by zero and everything will fall apart.'
369
370 UNION ALL
371 SELECT N'@OutputSchemaName',
372 N'NVARCHAR(258)',
373 N'The output schema. If this does not exist SQL Server will divide by zero and everything will fall apart.'
374
375 UNION ALL
376 SELECT N'@OutputTableName',
377 N'NVARCHAR(258)',
378 N'The output table. If this does not exist, it will be created for you.'
379
380 UNION ALL
381 SELECT N'@DurationFilter',
382 N'DECIMAL(38,4)',
383 N'Excludes queries with an average duration (in seconds) less than @DurationFilter.'
384
385 UNION ALL
386 SELECT N'@HideSummary',
387 N'BIT',
388 N'Hides the findings summary result set.'
389
390 UNION ALL
391 SELECT N'@IgnoreSystemDBs',
392 N'BIT',
393 N'Ignores plans found in the system databases (master, model, msdb, tempdb, and resourcedb)'
394
395 UNION ALL
396 SELECT N'@OnlyQueryHashes',
397 N'VARCHAR(MAX)',
398 N'A list of query hashes to query. All other query hashes will be ignored. Stored procedures and triggers will be ignored.'
399
400 UNION ALL
401 SELECT N'@IgnoreQueryHashes',
402 N'VARCHAR(MAX)',
403 N'A list of query hashes to ignore.'
404
405 UNION ALL
406 SELECT N'@OnlySqlHandles',
407 N'VARCHAR(MAX)',
408 N'One or more sql_handles to use for filtering results.'
409
410 UNION ALL
411 SELECT N'@IgnoreSqlHandles',
412 N'VARCHAR(MAX)',
413 N'One or more sql_handles to ignore.'
414
415 UNION ALL
416 SELECT N'@DatabaseName',
417 N'NVARCHAR(128)',
418 N'A database name which is used for filtering results.'
419
420 UNION ALL
421 SELECT N'@StoredProcName',
422 N'NVARCHAR(128)',
423 N'Name of stored procedure you want to find plans for.'
424
425 UNION ALL
426 SELECT N'@SlowlySearchPlansFor',
427 N'NVARCHAR(4000)',
428 N'String to search for in plan text. % wildcards allowed.'
429
430 UNION ALL
431 SELECT N'@BringThePain',
432 N'BIT',
433 N'This forces sp_BlitzCache to examine the entire plan cache. Be careful running this on servers with a lot of memory or a large execution plan cache.'
434
435 UNION ALL
436 SELECT N'@QueryFilter',
437 N'VARCHAR(10)',
438 N'Filter out stored procedures or statements. The default value is ''ALL''. Allowed values are ''procedures'', ''statements'', ''functions'', or ''all'' (any variation in capitalization is acceptable).'
439
440 UNION ALL
441 SELECT N'@Reanalyze',
442 N'BIT',
443 N'The default is 0. When set to 0, sp_BlitzCache will re-evalute the plan cache. Set this to 1 to reanalyze existing results'
444
445 UNION ALL
446 SELECT N'@MinimumExecutionCount',
447 N'INT',
448 N'Queries with fewer than this number of executions will be omitted from results.'
449
450 UNION ALL
451 SELECT N'@Debug',
452 N'BIT',
453 N'Setting this to 1 will print dynamic SQL and select data from all tables used.'
454
455 UNION ALL
456 SELECT N'@MinutesBack',
457 N'INT',
458 N'How many minutes back to begin plan cache analysis. If you put in a positive number, we''ll flip it to negtive.';
459
460
461 /* Column definitions */
462 SELECT N'# Executions' AS [Column Name],
463 N'BIGINT' AS [Data Type],
464 N'The number of executions of this particular query. This is computed across statements, procedures, and triggers and aggregated by the SQL handle.' AS [Column Description]
465
466 UNION ALL
467 SELECT N'Executions / Minute',
468 N'MONEY',
469 N'Number of executions per minute - calculated for the life of the current plan. Plan life is the last execution time minus the plan creation time.'
470
471 UNION ALL
472 SELECT N'Execution Weight',
473 N'MONEY',
474 N'An arbitrary metric of total "execution-ness". A weight of 2 is "one more" than a weight of 1.'
475
476 UNION ALL
477 SELECT N'Database',
478 N'sysname',
479 N'The name of the database where the plan was encountered. If the database name cannot be determined for some reason, a value of NA will be substituted. A value of 32767 indicates the plan comes from ResourceDB.'
480
481 UNION ALL
482 SELECT N'Total CPU',
483 N'BIGINT',
484 N'Total CPU time, reported in milliseconds, that was consumed by all executions of this query since the last compilation.'
485
486 UNION ALL
487 SELECT N'Avg CPU',
488 N'BIGINT',
489 N'Average CPU time, reported in milliseconds, consumed by each execution of this query since the last compilation.'
490
491 UNION ALL
492 SELECT N'CPU Weight',
493 N'MONEY',
494 N'An arbitrary metric of total "CPU-ness". A weight of 2 is "one more" than a weight of 1.'
495
496 UNION ALL
497 SELECT N'Total Duration',
498 N'BIGINT',
499 N'Total elapsed time, reported in milliseconds, consumed by all executions of this query since last compilation.'
500
501 UNION ALL
502 SELECT N'Avg Duration',
503 N'BIGINT',
504 N'Average elapsed time, reported in milliseconds, consumed by each execution of this query since the last compilation.'
505
506 UNION ALL
507 SELECT N'Duration Weight',
508 N'MONEY',
509 N'An arbitrary metric of total "Duration-ness". A weight of 2 is "one more" than a weight of 1.'
510
511 UNION ALL
512 SELECT N'Total Reads',
513 N'BIGINT',
514 N'Total logical reads performed by this query since last compilation.'
515
516 UNION ALL
517 SELECT N'Average Reads',
518 N'BIGINT',
519 N'Average logical reads performed by each execution of this query since the last compilation.'
520
521 UNION ALL
522 SELECT N'Read Weight',
523 N'MONEY',
524 N'An arbitrary metric of "Read-ness". A weight of 2 is "one more" than a weight of 1.'
525
526 UNION ALL
527 SELECT N'Total Writes',
528 N'BIGINT',
529 N'Total logical writes performed by this query since last compilation.'
530
531 UNION ALL
532 SELECT N'Average Writes',
533 N'BIGINT',
534 N'Average logical writes performed by each execution this query since last compilation.'
535
536 UNION ALL
537 SELECT N'Write Weight',
538 N'MONEY',
539 N'An arbitrary metric of "Write-ness". A weight of 2 is "one more" than a weight of 1.'
540
541 UNION ALL
542 SELECT N'Query Type',
543 N'NVARCHAR(258)',
544 N'The type of query being examined. This can be "Procedure", "Statement", or "Trigger".'
545
546 UNION ALL
547 SELECT N'Query Text',
548 N'NVARCHAR(4000)',
549 N'The text of the query. This may be truncated by either SQL Server or by sp_BlitzCache(tm) for display purposes.'
550
551 UNION ALL
552 SELECT N'% Executions (Type)',
553 N'MONEY',
554 N'Percent of executions relative to the type of query - e.g. 17.2% of all stored procedure executions.'
555
556 UNION ALL
557 SELECT N'% CPU (Type)',
558 N'MONEY',
559 N'Percent of CPU time consumed by this query for a given type of query - e.g. 22% of CPU of all stored procedures executed.'
560
561 UNION ALL
562 SELECT N'% Duration (Type)',
563 N'MONEY',
564 N'Percent of elapsed time consumed by this query for a given type of query - e.g. 12% of all statements executed.'
565
566 UNION ALL
567 SELECT N'% Reads (Type)',
568 N'MONEY',
569 N'Percent of reads consumed by this query for a given type of query - e.g. 34.2% of all stored procedures executed.'
570
571 UNION ALL
572 SELECT N'% Writes (Type)',
573 N'MONEY',
574 N'Percent of writes performed by this query for a given type of query - e.g. 43.2% of all statements executed.'
575
576 UNION ALL
577 SELECT N'Total Rows',
578 N'BIGINT',
579 N'Total number of rows returned for all executions of this query. This only applies to query level stats, not stored procedures or triggers.'
580
581 UNION ALL
582 SELECT N'Average Rows',
583 N'MONEY',
584 N'Average number of rows returned by each execution of the query.'
585
586 UNION ALL
587 SELECT N'Min Rows',
588 N'BIGINT',
589 N'The minimum number of rows returned by any execution of this query.'
590
591 UNION ALL
592 SELECT N'Max Rows',
593 N'BIGINT',
594 N'The maximum number of rows returned by any execution of this query.'
595
596 UNION ALL
597 SELECT N'MinGrantKB',
598 N'BIGINT',
599 N'The minimum memory grant the query received in kb.'
600
601 UNION ALL
602 SELECT N'MaxGrantKB',
603 N'BIGINT',
604 N'The maximum memory grant the query received in kb.'
605
606 UNION ALL
607 SELECT N'MinUsedGrantKB',
608 N'BIGINT',
609 N'The minimum used memory grant the query received in kb.'
610
611 UNION ALL
612 SELECT N'MaxUsedGrantKB',
613 N'BIGINT',
614 N'The maximum used memory grant the query received in kb.';
615
616 SELECT N'MinSpills',
617 N'BIGINT',
618 N'The minimum amount this query has spilled to tempdb in 8k pages.'
619
620 UNION ALL
621 SELECT N'MaxSpills',
622 N'BIGINT',
623 N'The maximum amount this query has spilled to tempdb in 8k pages.'
624
625 UNION ALL
626 SELECT N'TotalSpills',
627 N'BIGINT',
628 N'The total amount this query has spilled to tempdb in 8k pages.'
629
630 UNION ALL
631 SELECT N'AvgSpills',
632 N'BIGINT',
633 N'The average amount this query has spilled to tempdb in 8k pages.'
634
635 UNION ALL
636 SELECT N'PercentMemoryGrantUsed',
637 N'MONEY',
638 N'Result of dividing the maximum grant used by the minimum granted.'
639
640 UNION ALL
641 SELECT N'AvgMaxMemoryGrant',
642 N'MONEY',
643 N'The average maximum memory grant for a query.'
644
645 UNION ALL
646 SELECT N'# Plans',
647 N'INT',
648 N'The total number of execution plans found that match a given query.'
649
650 UNION ALL
651 SELECT N'# Distinct Plans',
652 N'INT',
653 N'The number of distinct execution plans that match a given query. '
654 + NCHAR(13) + NCHAR(10)
655 + N'This may be caused by running the same query across multiple databases or because of a lack of proper parameterization in the database.'
656
657 UNION ALL
658 SELECT N'Created At',
659 N'DATETIME',
660 N'Time that the execution plan was last compiled.'
661
662 UNION ALL
663 SELECT N'Last Execution',
664 N'DATETIME',
665 N'The last time that this query was executed.'
666
667 UNION ALL
668 SELECT N'Query Plan',
669 N'XML',
670 N'The query plan. Click to display a graphical plan or, if you need to patch SSMS, a pile of XML.'
671
672 UNION ALL
673 SELECT N'Plan Handle',
674 N'VARBINARY(64)',
675 N'An arbitrary identifier referring to the compiled plan this query is a part of.'
676
677 UNION ALL
678 SELECT N'SQL Handle',
679 N'VARBINARY(64)',
680 N'An arbitrary identifier referring to a batch or stored procedure that this query is a part of.'
681
682 UNION ALL
683 SELECT N'Query Hash',
684 N'BINARY(8)',
685 N'A hash of the query. Queries with the same query hash have similar logic but only differ by literal values or database.'
686
687 UNION ALL
688 SELECT N'Warnings',
689 N'VARCHAR(MAX)',
690 N'A list of individual warnings generated by this query.' ;
691
692
693
694 /* Configuration table description */
695 SELECT N'Frequent Execution Threshold' AS [Configuration Parameter] ,
696 N'100' AS [Default Value] ,
697 N'Executions / Minute' AS [Unit of Measure] ,
698 N'Executions / Minute before a "Frequent Execution Threshold" warning is triggered.' AS [Description]
699
700 UNION ALL
701 SELECT N'Parameter Sniffing Variance Percent' ,
702 N'30' ,
703 N'Percent' ,
704 N'Variance required between min/max values and average values before a "Parameter Sniffing" warning is triggered. Applies to worker time and returned rows.'
705
706 UNION ALL
707 SELECT N'Parameter Sniffing IO Threshold' ,
708 N'100,000' ,
709 N'Logical reads' ,
710 N'Minimum number of average logical reads before parameter sniffing checks are evaluated.'
711
712 UNION ALL
713 SELECT N'Cost Threshold for Parallelism Warning' AS [Configuration Parameter] ,
714 N'10' ,
715 N'Percent' ,
716 N'Trigger a "Nearly Parallel" warning when a query''s cost is within X percent of the cost threshold for parallelism.'
717
718 UNION ALL
719 SELECT N'Long Running Query Warning' AS [Configuration Parameter] ,
720 N'300' ,
721 N'Seconds' ,
722 N'Triggers a "Long Running Query Warning" when average duration, max CPU time, or max clock time is higher than this number.'
723
724 UNION ALL
725 SELECT N'Unused Memory Grant Warning' AS [Configuration Parameter] ,
726 N'10' ,
727 N'Percent' ,
728 N'Triggers an "Unused Memory Grant Warning" when a query uses >= X percent of its memory grant.';
729 RETURN;
730END;
731
732/*Validate version*/
733IF (
734SELECT
735 CASE
736 WHEN CONVERT(NVARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '8%' THEN 0
737 WHEN CONVERT(NVARCHAR(128), SERVERPROPERTY ('PRODUCTVERSION')) LIKE '9%' THEN 0
738 ELSE 1
739 END
740) = 0
741BEGIN
742 DECLARE @version_msg VARCHAR(8000);
743 SELECT @version_msg = 'Sorry, sp_BlitzCache doesn''t work on versions of SQL prior to 2008.' + REPLICATE(CHAR(13), 7933);
744 PRINT @version_msg;
745 RETURN;
746END;
747
748/* Set @Top based on sort */
749IF (
750 @Top IS NULL
751 AND LOWER(@SortOrder) IN ( 'all', 'all sort' )
752 )
753 BEGIN
754 SET @Top = 5;
755 END;
756
757IF (
758 @Top IS NULL
759 AND LOWER(@SortOrder) NOT IN ( 'all', 'all sort' )
760 )
761 BEGIN
762 SET @Top = 10;
763 END;
764
765/* validate user inputs */
766IF @Top IS NULL
767 OR @SortOrder IS NULL
768 OR @QueryFilter IS NULL
769 OR @Reanalyze IS NULL
770BEGIN
771 RAISERROR(N'Several parameters (@Top, @SortOrder, @QueryFilter, @renalyze) are required. Do not set them to NULL. Please try again.', 16, 1) WITH NOWAIT;
772 RETURN;
773END;
774
775RAISERROR(N'Checking @MinutesBack validity.', 0, 1) WITH NOWAIT;
776IF @MinutesBack IS NOT NULL
777 BEGIN
778 IF @MinutesBack > 0
779 BEGIN
780 RAISERROR(N'Setting @MinutesBack to a negative number', 0, 1) WITH NOWAIT;
781 SET @MinutesBack *=-1;
782 END;
783 IF @MinutesBack = 0
784 BEGIN
785 RAISERROR(N'@MinutesBack can''t be 0, setting to -1', 0, 1) WITH NOWAIT;
786 SET @MinutesBack = -1;
787 END;
788 END;
789
790
791RAISERROR(N'Creating temp tables for results and warnings.', 0, 1) WITH NOWAIT;
792
793IF OBJECT_ID('tempdb.dbo.##BlitzCacheResults') IS NULL
794BEGIN
795 CREATE TABLE ##BlitzCacheResults (
796 SPID INT,
797 ID INT IDENTITY(1,1),
798 CheckID INT,
799 Priority TINYINT,
800 FindingsGroup VARCHAR(50),
801 Finding VARCHAR(200),
802 URL VARCHAR(200),
803 Details VARCHAR(4000)
804 );
805END;
806
807IF OBJECT_ID('tempdb.dbo.##BlitzCacheProcs') IS NULL
808BEGIN
809 CREATE TABLE ##BlitzCacheProcs (
810 SPID INT ,
811 QueryType NVARCHAR(258),
812 DatabaseName sysname,
813 AverageCPU DECIMAL(38,4),
814 AverageCPUPerMinute DECIMAL(38,4),
815 TotalCPU DECIMAL(38,4),
816 PercentCPUByType MONEY,
817 PercentCPU MONEY,
818 AverageDuration DECIMAL(38,4),
819 TotalDuration DECIMAL(38,4),
820 PercentDuration MONEY,
821 PercentDurationByType MONEY,
822 AverageReads BIGINT,
823 TotalReads BIGINT,
824 PercentReads MONEY,
825 PercentReadsByType MONEY,
826 ExecutionCount BIGINT,
827 PercentExecutions MONEY,
828 PercentExecutionsByType MONEY,
829 ExecutionsPerMinute MONEY,
830 TotalWrites BIGINT,
831 AverageWrites MONEY,
832 PercentWrites MONEY,
833 PercentWritesByType MONEY,
834 WritesPerMinute MONEY,
835 PlanCreationTime DATETIME,
836 PlanCreationTimeHours AS DATEDIFF(HOUR, PlanCreationTime, SYSDATETIME()),
837 LastExecutionTime DATETIME,
838 PlanHandle VARBINARY(64),
839 [Remove Plan Handle From Cache] AS
840 CASE WHEN [PlanHandle] IS NOT NULL
841 THEN 'DBCC FREEPROCCACHE (' + CONVERT(VARCHAR(128), [PlanHandle], 1) + ');'
842 ELSE 'N/A' END,
843 SqlHandle VARBINARY(64),
844 [Remove SQL Handle From Cache] AS
845 CASE WHEN [SqlHandle] IS NOT NULL
846 THEN 'DBCC FREEPROCCACHE (' + CONVERT(VARCHAR(128), [SqlHandle], 1) + ');'
847 ELSE 'N/A' END,
848 [SQL Handle More Info] AS
849 CASE WHEN [SqlHandle] IS NOT NULL
850 THEN 'EXEC sp_BlitzCache @OnlySqlHandles = ''' + CONVERT(VARCHAR(128), [SqlHandle], 1) + '''; '
851 ELSE 'N/A' END,
852 QueryHash BINARY(8),
853 [Query Hash More Info] AS
854 CASE WHEN [QueryHash] IS NOT NULL
855 THEN 'EXEC sp_BlitzCache @OnlyQueryHashes = ''' + CONVERT(VARCHAR(32), [QueryHash], 1) + '''; '
856 ELSE 'N/A' END,
857 QueryPlanHash BINARY(8),
858 StatementStartOffset INT,
859 StatementEndOffset INT,
860 MinReturnedRows BIGINT,
861 MaxReturnedRows BIGINT,
862 AverageReturnedRows MONEY,
863 TotalReturnedRows BIGINT,
864 LastReturnedRows BIGINT,
865 MinGrantKB BIGINT,
866 MaxGrantKB BIGINT,
867 MinUsedGrantKB BIGINT,
868 MaxUsedGrantKB BIGINT,
869 PercentMemoryGrantUsed MONEY,
870 AvgMaxMemoryGrant MONEY,
871 MinSpills BIGINT,
872 MaxSpills BIGINT,
873 TotalSpills BIGINT,
874 AvgSpills MONEY,
875 QueryText NVARCHAR(MAX),
876 QueryPlan XML,
877 /* these next four columns are the total for the type of query.
878 don't actually use them for anything apart from math by type.
879 */
880 TotalWorkerTimeForType BIGINT,
881 TotalElapsedTimeForType BIGINT,
882 TotalReadsForType BIGINT,
883 TotalExecutionCountForType BIGINT,
884 TotalWritesForType BIGINT,
885 NumberOfPlans INT,
886 NumberOfDistinctPlans INT,
887 SerialDesiredMemory FLOAT,
888 SerialRequiredMemory FLOAT,
889 CachedPlanSize FLOAT,
890 CompileTime FLOAT,
891 CompileCPU FLOAT ,
892 CompileMemory FLOAT ,
893 MaxCompileMemory FLOAT ,
894 min_worker_time BIGINT,
895 max_worker_time BIGINT,
896 is_forced_plan BIT,
897 is_forced_parameterized BIT,
898 is_cursor BIT,
899 is_optimistic_cursor BIT,
900 is_forward_only_cursor BIT,
901 is_fast_forward_cursor BIT,
902 is_cursor_dynamic BIT,
903 is_parallel BIT,
904 is_forced_serial BIT,
905 is_key_lookup_expensive BIT,
906 key_lookup_cost FLOAT,
907 is_remote_query_expensive BIT,
908 remote_query_cost FLOAT,
909 frequent_execution BIT,
910 parameter_sniffing BIT,
911 unparameterized_query BIT,
912 near_parallel BIT,
913 plan_warnings BIT,
914 plan_multiple_plans BIT,
915 long_running BIT,
916 downlevel_estimator BIT,
917 implicit_conversions BIT,
918 busy_loops BIT,
919 tvf_join BIT,
920 tvf_estimate BIT,
921 compile_timeout BIT,
922 compile_memory_limit_exceeded BIT,
923 warning_no_join_predicate BIT,
924 QueryPlanCost FLOAT,
925 missing_index_count INT,
926 unmatched_index_count INT,
927 min_elapsed_time BIGINT,
928 max_elapsed_time BIGINT,
929 age_minutes MONEY,
930 age_minutes_lifetime MONEY,
931 is_trivial BIT,
932 trace_flags_session VARCHAR(1000),
933 is_unused_grant BIT,
934 function_count INT,
935 clr_function_count INT,
936 is_table_variable BIT,
937 no_stats_warning BIT,
938 relop_warnings BIT,
939 is_table_scan BIT,
940 backwards_scan BIT,
941 forced_index BIT,
942 forced_seek BIT,
943 forced_scan BIT,
944 columnstore_row_mode BIT,
945 is_computed_scalar BIT ,
946 is_sort_expensive BIT,
947 sort_cost FLOAT,
948 is_computed_filter BIT,
949 op_name VARCHAR(100) NULL,
950 index_insert_count INT NULL,
951 index_update_count INT NULL,
952 index_delete_count INT NULL,
953 cx_insert_count INT NULL,
954 cx_update_count INT NULL,
955 cx_delete_count INT NULL,
956 table_insert_count INT NULL,
957 table_update_count INT NULL,
958 table_delete_count INT NULL,
959 index_ops AS (index_insert_count + index_update_count + index_delete_count +
960 cx_insert_count + cx_update_count + cx_delete_count +
961 table_insert_count + table_update_count + table_delete_count),
962 is_row_level BIT,
963 is_spatial BIT,
964 index_dml BIT,
965 table_dml BIT,
966 long_running_low_cpu BIT,
967 low_cost_high_cpu BIT,
968 stale_stats BIT,
969 is_adaptive BIT,
970 index_spool_cost FLOAT,
971 index_spool_rows FLOAT,
972 is_spool_expensive BIT,
973 is_spool_more_rows BIT,
974 estimated_rows FLOAT,
975 is_bad_estimate BIT,
976 is_paul_white_electric BIT,
977 is_row_goal BIT,
978 is_big_spills BIT,
979 is_mstvf BIT,
980 is_mm_join BIT,
981 is_nonsargable BIT,
982 implicit_conversion_info XML,
983 cached_execution_parameters XML,
984 missing_indexes XML,
985 SetOptions VARCHAR(MAX),
986 Warnings VARCHAR(MAX)
987 );
988END;
989
990DECLARE @DurationFilter_i INT,
991 @MinMemoryPerQuery INT,
992 @msg NVARCHAR(4000),
993 @NoobSaibot BIT = 0;
994
995IF @SortOrder = 'sp_BlitzIndex'
996BEGIN
997 RAISERROR(N'OUTSTANDING!', 0, 1) WITH NOWAIT;
998 SET @SortOrder = 'reads';
999 SET @NoobSaibot = 1;
1000
1001END
1002
1003
1004IF @BringThePain = 1
1005 BEGIN
1006 RAISERROR(N'You have chosen to bring the pain. Setting top to 2147483647.', 0, 1) WITH NOWAIT;
1007 SET @Top = 2147483647;
1008 END;
1009
1010/* Change duration from seconds to milliseconds */
1011IF @DurationFilter IS NOT NULL
1012 BEGIN
1013 RAISERROR(N'Converting Duration Filter to milliseconds', 0, 1) WITH NOWAIT;
1014 SET @DurationFilter_i = CAST((@DurationFilter * 1000.0) AS INT);
1015 END;
1016
1017RAISERROR(N'Checking database validity', 0, 1) WITH NOWAIT;
1018SET @DatabaseName = LTRIM(RTRIM(@DatabaseName)) ;
1019
1020IF SERVERPROPERTY('EngineEdition') IN (5, 6, 8) AND DB_NAME() <> @DatabaseName
1021BEGIN
1022 RAISERROR('You specified a database name other than the current database, but Azure SQL DB does not allow you to change databases. Execute sp_BlitzCache from the database you want to analyze.', 16, 1);
1023 RETURN;
1024END;
1025IF (DB_ID(@DatabaseName)) IS NULL AND @DatabaseName <> N''
1026BEGIN
1027 RAISERROR('The database you specified does not exist. Please check the name and try again.', 16, 1);
1028 RETURN;
1029END;
1030IF (SELECT DATABASEPROPERTYEX(ISNULL(@DatabaseName, 'master'), 'Collation')) IS NULL AND SERVERPROPERTY('EngineEdition') NOT IN (5, 6, 8)
1031BEGIN
1032 RAISERROR('The database you specified is not readable. Please check the name and try again. Better yet, check your server.', 16, 1);
1033 RETURN;
1034END;
1035
1036SELECT @MinMemoryPerQuery = CONVERT(INT, c.value) FROM sys.configurations AS c WHERE c.name = 'min memory per query (KB)';
1037
1038SET @SortOrder = LOWER(@SortOrder);
1039SET @SortOrder = REPLACE(REPLACE(@SortOrder, 'average', 'avg'), '.', '');
1040
1041SET @SortOrder = CASE
1042 WHEN @SortOrder IN ('executions per minute','execution per minute','executions / minute','execution / minute','xpm') THEN 'avg executions'
1043 WHEN @SortOrder IN ('recent compilations','recent compilation','compile') THEN 'compiles'
1044 WHEN @SortOrder IN ('read') THEN 'reads'
1045 WHEN @SortOrder IN ('avg read') THEN 'avg reads'
1046 WHEN @SortOrder IN ('write') THEN 'writes'
1047 WHEN @SortOrder IN ('avg write') THEN 'avg writes'
1048 WHEN @SortOrder IN ('memory grants') THEN 'memory grant'
1049 WHEN @SortOrder IN ('avg memory grants') THEN 'avg memory grant'
1050 WHEN @SortOrder IN ('spill') THEN 'spills'
1051 WHEN @SortOrder IN ('avg spill') THEN 'avg spills'
1052 WHEN @SortOrder IN ('execution') THEN 'executions'
1053 ELSE @SortOrder END
1054
1055RAISERROR(N'Checking sort order', 0, 1) WITH NOWAIT;
1056IF @SortOrder NOT IN ('cpu', 'avg cpu', 'reads', 'avg reads', 'writes', 'avg writes',
1057 'duration', 'avg duration', 'executions', 'avg executions',
1058 'compiles', 'memory grant', 'avg memory grant',
1059 'spills', 'avg spills', 'all', 'all avg', 'sp_BlitzIndex')
1060 BEGIN
1061 RAISERROR(N'Invalid sort order chosen, reverting to cpu', 16, 1) WITH NOWAIT;
1062 SET @SortOrder = 'cpu';
1063 END;
1064
1065SELECT @OutputDatabaseName = QUOTENAME(@OutputDatabaseName),
1066 @OutputSchemaName = QUOTENAME(@OutputSchemaName),
1067 @OutputTableName = QUOTENAME(@OutputTableName);
1068
1069SET @QueryFilter = LOWER(@QueryFilter);
1070
1071IF LEFT(@QueryFilter, 3) NOT IN ('all', 'sta', 'pro', 'fun')
1072 BEGIN
1073 RAISERROR(N'Invalid query filter chosen. Reverting to all.', 0, 1) WITH NOWAIT;
1074 SET @QueryFilter = 'all';
1075 END;
1076
1077IF @SkipAnalysis = 1
1078 BEGIN
1079 RAISERROR(N'Skip Analysis set to 1, hiding Summary', 0, 1) WITH NOWAIT;
1080 SET @HideSummary = 1;
1081 END;
1082
1083IF @Reanalyze = 1 AND OBJECT_ID('tempdb..##BlitzCacheResults') IS NULL
1084 BEGIN
1085 RAISERROR(N'##BlitzCacheResults does not exist, can''t reanalyze', 0, 1) WITH NOWAIT;
1086 SET @Reanalyze = 0;
1087 END;
1088
1089IF @Reanalyze = 0
1090 BEGIN
1091 RAISERROR(N'Cleaning up old warnings for your SPID', 0, 1) WITH NOWAIT;
1092 DELETE ##BlitzCacheResults
1093 WHERE SPID = @@SPID
1094 OPTION (RECOMPILE) ;
1095 RAISERROR(N'Cleaning up old plans for your SPID', 0, 1) WITH NOWAIT;
1096 DELETE ##BlitzCacheProcs
1097 WHERE SPID = @@SPID
1098 OPTION (RECOMPILE) ;
1099 END;
1100
1101IF @Reanalyze = 1
1102 BEGIN
1103 RAISERROR(N'Reanalyzing current data, skipping to results', 0, 1) WITH NOWAIT;
1104 GOTO Results;
1105 END;
1106
1107IF @SortOrder IN ('all', 'all avg')
1108 BEGIN
1109 RAISERROR(N'Checking all sort orders, please be patient', 0, 1) WITH NOWAIT;
1110 GOTO AllSorts;
1111 END;
1112
1113
1114RAISERROR(N'Creating temp tables for internal processing', 0, 1) WITH NOWAIT;
1115IF OBJECT_ID('tempdb..#only_query_hashes') IS NOT NULL
1116 DROP TABLE #only_query_hashes ;
1117
1118IF OBJECT_ID('tempdb..#ignore_query_hashes') IS NOT NULL
1119 DROP TABLE #ignore_query_hashes ;
1120
1121IF OBJECT_ID('tempdb..#only_sql_handles') IS NOT NULL
1122 DROP TABLE #only_sql_handles ;
1123
1124IF OBJECT_ID('tempdb..#ignore_sql_handles') IS NOT NULL
1125 DROP TABLE #ignore_sql_handles ;
1126
1127IF OBJECT_ID('tempdb..#p') IS NOT NULL
1128 DROP TABLE #p;
1129
1130IF OBJECT_ID ('tempdb..#checkversion') IS NOT NULL
1131 DROP TABLE #checkversion;
1132
1133IF OBJECT_ID ('tempdb..#configuration') IS NOT NULL
1134 DROP TABLE #configuration;
1135
1136IF OBJECT_ID ('tempdb..#stored_proc_info') IS NOT NULL
1137 DROP TABLE #stored_proc_info;
1138
1139IF OBJECT_ID ('tempdb..#plan_creation') IS NOT NULL
1140 DROP TABLE #plan_creation;
1141
1142IF OBJECT_ID ('tempdb..#est_rows') IS NOT NULL
1143 DROP TABLE #est_rows;
1144
1145IF OBJECT_ID ('tempdb..#plan_cost') IS NOT NULL
1146 DROP TABLE #plan_cost;
1147
1148IF OBJECT_ID ('tempdb..#proc_costs') IS NOT NULL
1149 DROP TABLE #proc_costs;
1150
1151IF OBJECT_ID ('tempdb..#stats_agg') IS NOT NULL
1152 DROP TABLE #stats_agg;
1153
1154IF OBJECT_ID ('tempdb..#trace_flags') IS NOT NULL
1155 DROP TABLE #trace_flags;
1156
1157IF OBJECT_ID('tempdb..#variable_info') IS NOT NULL
1158 DROP TABLE #variable_info;
1159
1160IF OBJECT_ID('tempdb..#conversion_info') IS NOT NULL
1161 DROP TABLE #conversion_info;
1162
1163IF OBJECT_ID('tempdb..#missing_index_xml') IS NOT NULL
1164 DROP TABLE #missing_index_xml;
1165
1166IF OBJECT_ID('tempdb..#missing_index_schema') IS NOT NULL
1167 DROP TABLE #missing_index_schema;
1168
1169IF OBJECT_ID('tempdb..#missing_index_usage') IS NOT NULL
1170 DROP TABLE #missing_index_usage;
1171
1172IF OBJECT_ID('tempdb..#missing_index_detail') IS NOT NULL
1173 DROP TABLE #missing_index_detail;
1174
1175IF OBJECT_ID('tempdb..#missing_index_pretty') IS NOT NULL
1176 DROP TABLE #missing_index_pretty;
1177
1178IF OBJECT_ID('tempdb..#index_spool_ugly') IS NOT NULL
1179 DROP TABLE #index_spool_ugly;
1180
1181CREATE TABLE #only_query_hashes (
1182 query_hash BINARY(8)
1183);
1184
1185CREATE TABLE #ignore_query_hashes (
1186 query_hash BINARY(8)
1187);
1188
1189CREATE TABLE #only_sql_handles (
1190 sql_handle VARBINARY(64)
1191);
1192
1193CREATE TABLE #ignore_sql_handles (
1194 sql_handle VARBINARY(64)
1195);
1196
1197CREATE TABLE #p (
1198 SqlHandle VARBINARY(64),
1199 TotalCPU BIGINT,
1200 TotalDuration BIGINT,
1201 TotalReads BIGINT,
1202 TotalWrites BIGINT,
1203 ExecutionCount BIGINT
1204);
1205
1206CREATE TABLE #checkversion (
1207 version NVARCHAR(128),
1208 common_version AS SUBSTRING(version, 1, CHARINDEX('.', version) + 1 ),
1209 major AS PARSENAME(CONVERT(VARCHAR(32), version), 4),
1210 minor AS PARSENAME(CONVERT(VARCHAR(32), version), 3),
1211 build AS PARSENAME(CONVERT(VARCHAR(32), version), 2),
1212 revision AS PARSENAME(CONVERT(VARCHAR(32), version), 1)
1213);
1214
1215CREATE TABLE #configuration (
1216 parameter_name VARCHAR(100),
1217 value DECIMAL(38,0)
1218);
1219
1220CREATE TABLE #plan_creation
1221(
1222 percent_24 DECIMAL(5, 2),
1223 percent_4 DECIMAL(5, 2),
1224 percent_1 DECIMAL(5, 2),
1225 total_plans INT,
1226 SPID INT
1227);
1228
1229CREATE TABLE #est_rows
1230(
1231 QueryHash BINARY(8),
1232 estimated_rows FLOAT
1233);
1234
1235CREATE TABLE #plan_cost
1236(
1237 QueryPlanCost FLOAT,
1238 SqlHandle VARBINARY(64),
1239 PlanHandle VARBINARY(64),
1240 QueryHash BINARY(8),
1241 QueryPlanHash BINARY(8)
1242);
1243
1244CREATE TABLE #proc_costs
1245(
1246 PlanTotalQuery FLOAT,
1247 PlanHandle VARBINARY(64),
1248 SqlHandle VARBINARY(64)
1249);
1250
1251CREATE TABLE #stats_agg
1252(
1253 SqlHandle VARBINARY(64),
1254 LastUpdate DATETIME2(7),
1255 ModificationCount BIGINT,
1256 SamplingPercent FLOAT,
1257 [Statistics] NVARCHAR(258),
1258 [Table] NVARCHAR(258),
1259 [Schema] NVARCHAR(258),
1260 [Database] NVARCHAR(258),
1261);
1262
1263CREATE TABLE #trace_flags
1264(
1265 SqlHandle VARBINARY(64),
1266 QueryHash BINARY(8),
1267 global_trace_flags VARCHAR(1000),
1268 session_trace_flags VARCHAR(1000)
1269);
1270
1271CREATE TABLE #stored_proc_info
1272(
1273 SPID INT,
1274 SqlHandle VARBINARY(64),
1275 QueryHash BINARY(8),
1276 variable_name NVARCHAR(258),
1277 variable_datatype NVARCHAR(258),
1278 converted_column_name NVARCHAR(258),
1279 compile_time_value NVARCHAR(258),
1280 proc_name NVARCHAR(1000),
1281 column_name NVARCHAR(4000),
1282 converted_to NVARCHAR(258),
1283 set_options NVARCHAR(1000)
1284);
1285
1286CREATE TABLE #variable_info
1287(
1288 SPID INT,
1289 QueryHash BINARY(8),
1290 SqlHandle VARBINARY(64),
1291 proc_name NVARCHAR(1000),
1292 variable_name NVARCHAR(258),
1293 variable_datatype NVARCHAR(258),
1294 compile_time_value NVARCHAR(258)
1295);
1296
1297CREATE TABLE #conversion_info
1298(
1299 SPID INT,
1300 QueryHash BINARY(8),
1301 SqlHandle VARBINARY(64),
1302 proc_name NVARCHAR(258),
1303 expression NVARCHAR(4000),
1304 at_charindex AS CHARINDEX('@', expression),
1305 bracket_charindex AS CHARINDEX(']', expression, CHARINDEX('@', expression)) - CHARINDEX('@', expression),
1306 comma_charindex AS CHARINDEX(',', expression) + 1,
1307 second_comma_charindex AS
1308 CHARINDEX(',', expression, CHARINDEX(',', expression) + 1) - CHARINDEX(',', expression) - 1,
1309 equal_charindex AS CHARINDEX('=', expression) + 1,
1310 paren_charindex AS CHARINDEX('(', expression) + 1,
1311 comma_paren_charindex AS
1312 CHARINDEX(',', expression, CHARINDEX('(', expression) + 1) - CHARINDEX('(', expression) - 1,
1313 convert_implicit_charindex AS CHARINDEX('=CONVERT_IMPLICIT', expression)
1314);
1315
1316
1317CREATE TABLE #missing_index_xml
1318(
1319 QueryHash BINARY(8),
1320 SqlHandle VARBINARY(64),
1321 impact FLOAT,
1322 index_xml XML
1323);
1324
1325
1326CREATE TABLE #missing_index_schema
1327(
1328 QueryHash BINARY(8),
1329 SqlHandle VARBINARY(64),
1330 impact FLOAT,
1331 database_name NVARCHAR(128),
1332 schema_name NVARCHAR(128),
1333 table_name NVARCHAR(128),
1334 index_xml XML
1335);
1336
1337
1338CREATE TABLE #missing_index_usage
1339(
1340 QueryHash BINARY(8),
1341 SqlHandle VARBINARY(64),
1342 impact FLOAT,
1343 database_name NVARCHAR(128),
1344 schema_name NVARCHAR(128),
1345 table_name NVARCHAR(128),
1346 usage NVARCHAR(128),
1347 index_xml XML
1348);
1349
1350
1351CREATE TABLE #missing_index_detail
1352(
1353 QueryHash BINARY(8),
1354 SqlHandle VARBINARY(64),
1355 impact FLOAT,
1356 database_name NVARCHAR(128),
1357 schema_name NVARCHAR(128),
1358 table_name NVARCHAR(128),
1359 usage NVARCHAR(128),
1360 column_name NVARCHAR(128)
1361);
1362
1363
1364CREATE TABLE #missing_index_pretty
1365(
1366 QueryHash BINARY(8),
1367 SqlHandle VARBINARY(64),
1368 impact FLOAT,
1369 database_name NVARCHAR(128),
1370 schema_name NVARCHAR(128),
1371 table_name NVARCHAR(128),
1372 equality NVARCHAR(MAX),
1373 inequality NVARCHAR(MAX),
1374 [include] NVARCHAR(MAX),
1375 executions NVARCHAR(128),
1376 query_cost NVARCHAR(128),
1377 creation_hours NVARCHAR(128),
1378 is_spool BIT,
1379 details AS N'/* '
1380 + CHAR(10)
1381 + CASE is_spool
1382 WHEN 0
1383 THEN N'The Query Processor estimates that implementing the '
1384 ELSE N'We estimate that implementing the '
1385 END
1386 + N'following index could improve query cost (' + query_cost + N')'
1387 + CHAR(10)
1388 + N'by '
1389 + CONVERT(NVARCHAR(30), impact)
1390 + N'% for ' + executions + N' executions of the query'
1391 + N' over the last ' +
1392 CASE WHEN creation_hours < 24
1393 THEN creation_hours + N' hours.'
1394 WHEN creation_hours = 24
1395 THEN ' 1 day.'
1396 WHEN creation_hours > 24
1397 THEN (CONVERT(NVARCHAR(128), creation_hours / 24)) + N' days.'
1398 ELSE N''
1399 END
1400 + CHAR(10)
1401 + N'*/'
1402 + CHAR(10) + CHAR(13)
1403 + N'/* '
1404 + CHAR(10)
1405 + N'USE '
1406 + database_name
1407 + CHAR(10)
1408 + N'GO'
1409 + CHAR(10) + CHAR(13)
1410 + N'CREATE NONCLUSTERED INDEX ix_'
1411 + ISNULL(REPLACE(REPLACE(REPLACE(equality,'[', ''), ']', ''), ', ', '_'), '')
1412 + ISNULL(REPLACE(REPLACE(REPLACE(inequality,'[', ''), ']', ''), ', ', '_'), '')
1413 + CASE WHEN [include] IS NOT NULL THEN + N'_Includes' ELSE N'' END
1414 + CHAR(10)
1415 + N' ON '
1416 + schema_name
1417 + N'.'
1418 + table_name
1419 + N' (' +
1420 + CASE WHEN equality IS NOT NULL
1421 THEN equality
1422 + CASE WHEN inequality IS NOT NULL
1423 THEN N', ' + inequality
1424 ELSE N''
1425 END
1426 ELSE inequality
1427 END
1428 + N')'
1429 + CHAR(10)
1430 + CASE WHEN include IS NOT NULL
1431 THEN N'INCLUDE (' + include + N') WITH (FILLFACTOR=100, ONLINE=?, SORT_IN_TEMPDB=?, DATA_COMPRESSION=?);'
1432 ELSE N' WITH (FILLFACTOR=100, ONLINE=?, SORT_IN_TEMPDB=?, DATA_COMPRESSION=?);'
1433 END
1434 + CHAR(10)
1435 + N'GO'
1436 + CHAR(10)
1437 + N'*/'
1438);
1439
1440
1441CREATE TABLE #index_spool_ugly
1442(
1443 QueryHash BINARY(8),
1444 SqlHandle VARBINARY(64),
1445 impact FLOAT,
1446 database_name NVARCHAR(128),
1447 schema_name NVARCHAR(128),
1448 table_name NVARCHAR(128),
1449 equality NVARCHAR(MAX),
1450 inequality NVARCHAR(MAX),
1451 [include] NVARCHAR(MAX),
1452 executions NVARCHAR(128),
1453 query_cost NVARCHAR(128),
1454 creation_hours NVARCHAR(128)
1455);
1456
1457
1458RAISERROR(N'Checking plan cache age', 0, 1) WITH NOWAIT;
1459WITH x AS (
1460SELECT SUM(CASE WHEN DATEDIFF(HOUR, deqs.creation_time, SYSDATETIME()) <= 24 THEN 1 ELSE 0 END) AS [plans_24],
1461 SUM(CASE WHEN DATEDIFF(HOUR, deqs.creation_time, SYSDATETIME()) <= 4 THEN 1 ELSE 0 END) AS [plans_4],
1462 SUM(CASE WHEN DATEDIFF(HOUR, deqs.creation_time, SYSDATETIME()) <= 1 THEN 1 ELSE 0 END) AS [plans_1],
1463 COUNT(deqs.creation_time) AS [total_plans]
1464FROM sys.dm_exec_query_stats AS deqs
1465)
1466INSERT INTO #plan_creation ( percent_24, percent_4, percent_1, total_plans, SPID )
1467SELECT CONVERT(DECIMAL(3,2), NULLIF(x.plans_24, 0) / (1. * NULLIF(x.total_plans, 0))) * 100 AS [percent_24],
1468 CONVERT(DECIMAL(3,2), NULLIF(x.plans_4 , 0) / (1. * NULLIF(x.total_plans, 0))) * 100 AS [percent_4],
1469 CONVERT(DECIMAL(3,2), NULLIF(x.plans_1 , 0) / (1. * NULLIF(x.total_plans, 0))) * 100 AS [percent_1],
1470 x.total_plans,
1471 @@SPID AS SPID
1472FROM x
1473OPTION (RECOMPILE) ;
1474
1475
1476SET @OnlySqlHandles = LTRIM(RTRIM(@OnlySqlHandles)) ;
1477SET @OnlyQueryHashes = LTRIM(RTRIM(@OnlyQueryHashes)) ;
1478SET @IgnoreQueryHashes = LTRIM(RTRIM(@IgnoreQueryHashes)) ;
1479
1480DECLARE @individual VARCHAR(100) ;
1481
1482IF (@OnlySqlHandles IS NOT NULL AND @IgnoreSqlHandles IS NOT NULL)
1483BEGIN
1484RAISERROR('You shouldn''t need to ignore and filter on SqlHandle at the same time.', 0, 1) WITH NOWAIT;
1485RETURN;
1486END;
1487
1488IF (@StoredProcName IS NOT NULL AND (@OnlySqlHandles IS NOT NULL OR @IgnoreSqlHandles IS NOT NULL))
1489BEGIN
1490RAISERROR('You can''t filter on stored procedure name and SQL Handle.', 0, 1) WITH NOWAIT;
1491RETURN;
1492END;
1493
1494IF @OnlySqlHandles IS NOT NULL
1495 AND LEN(@OnlySqlHandles) > 0
1496BEGIN
1497 RAISERROR(N'Processing SQL Handles', 0, 1) WITH NOWAIT;
1498 SET @individual = '';
1499
1500 WHILE LEN(@OnlySqlHandles) > 0
1501 BEGIN
1502 IF PATINDEX('%,%', @OnlySqlHandles) > 0
1503 BEGIN
1504 SET @individual = SUBSTRING(@OnlySqlHandles, 0, PATINDEX('%,%',@OnlySqlHandles)) ;
1505
1506 INSERT INTO #only_sql_handles
1507 SELECT CAST('' AS XML).value('xs:hexBinary( substring(sql:variable("@individual"), sql:column("t.pos")) )', 'varbinary(max)')
1508 FROM (SELECT CASE SUBSTRING(@individual, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos)
1509 OPTION (RECOMPILE) ;
1510
1511 --SELECT CAST(SUBSTRING(@individual, 1, 2) AS BINARY(8));
1512
1513 SET @OnlySqlHandles = SUBSTRING(@OnlySqlHandles, LEN(@individual + ',') + 1, LEN(@OnlySqlHandles)) ;
1514 END;
1515 ELSE
1516 BEGIN
1517 SET @individual = @OnlySqlHandles;
1518 SET @OnlySqlHandles = NULL;
1519
1520 INSERT INTO #only_sql_handles
1521 SELECT CAST('' AS XML).value('xs:hexBinary( substring(sql:variable("@individual"), sql:column("t.pos")) )', 'varbinary(max)')
1522 FROM (SELECT CASE SUBSTRING(@individual, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos)
1523 OPTION (RECOMPILE) ;
1524
1525 --SELECT CAST(SUBSTRING(@individual, 1, 2) AS VARBINARY(MAX)) ;
1526 END;
1527 END;
1528END;
1529
1530IF @IgnoreSqlHandles IS NOT NULL
1531 AND LEN(@IgnoreSqlHandles) > 0
1532BEGIN
1533 RAISERROR(N'Processing SQL Handles To Ignore', 0, 1) WITH NOWAIT;
1534 SET @individual = '';
1535
1536 WHILE LEN(@IgnoreSqlHandles) > 0
1537 BEGIN
1538 IF PATINDEX('%,%', @IgnoreSqlHandles) > 0
1539 BEGIN
1540 SET @individual = SUBSTRING(@IgnoreSqlHandles, 0, PATINDEX('%,%',@IgnoreSqlHandles)) ;
1541
1542 INSERT INTO #ignore_sql_handles
1543 SELECT CAST('' AS XML).value('xs:hexBinary( substring(sql:variable("@individual"), sql:column("t.pos")) )', 'varbinary(max)')
1544 FROM (SELECT CASE SUBSTRING(@individual, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos)
1545 OPTION (RECOMPILE) ;
1546
1547 --SELECT CAST(SUBSTRING(@individual, 1, 2) AS BINARY(8));
1548
1549 SET @IgnoreSqlHandles = SUBSTRING(@IgnoreSqlHandles, LEN(@individual + ',') + 1, LEN(@IgnoreSqlHandles)) ;
1550 END;
1551 ELSE
1552 BEGIN
1553 SET @individual = @IgnoreSqlHandles;
1554 SET @IgnoreSqlHandles = NULL;
1555
1556 INSERT INTO #ignore_sql_handles
1557 SELECT CAST('' AS XML).value('xs:hexBinary( substring(sql:variable("@individual"), sql:column("t.pos")) )', 'varbinary(max)')
1558 FROM (SELECT CASE SUBSTRING(@individual, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos)
1559 OPTION (RECOMPILE) ;
1560
1561 --SELECT CAST(SUBSTRING(@individual, 1, 2) AS VARBINARY(MAX)) ;
1562 END;
1563 END;
1564END;
1565
1566IF @StoredProcName IS NOT NULL AND @StoredProcName <> N''
1567
1568BEGIN
1569 RAISERROR(N'Setting up filter for stored procedure name', 0, 1) WITH NOWAIT;
1570
1571 DECLARE @function_search_sql NVARCHAR(MAX) = N''
1572
1573 INSERT #only_sql_handles
1574 ( sql_handle )
1575 SELECT ISNULL(deps.sql_handle, CONVERT(VARBINARY(64),'0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'))
1576 FROM sys.dm_exec_procedure_stats AS deps
1577 WHERE OBJECT_NAME(deps.object_id, deps.database_id) = @StoredProcName
1578
1579 UNION ALL
1580
1581 SELECT ISNULL(dets.sql_handle, CONVERT(VARBINARY(64),'0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'))
1582 FROM sys.dm_exec_trigger_stats AS dets
1583 WHERE OBJECT_NAME(dets.object_id, dets.database_id) = @StoredProcName
1584 OPTION (RECOMPILE);
1585
1586 IF EXISTS (SELECT 1/0 FROM sys.all_objects AS o WHERE o.name = 'dm_exec_function_stats')
1587 BEGIN
1588 SET @function_search_sql = @function_search_sql + N'
1589 SELECT ISNULL(defs.sql_handle, CONVERT(VARBINARY(64),''0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000''))
1590 FROM sys.dm_exec_function_stats AS defs
1591 WHERE OBJECT_NAME(defs.object_id, defs.database_id) = @i_StoredProcName
1592 OPTION (RECOMPILE);
1593 '
1594 INSERT #only_sql_handles ( sql_handle )
1595 EXEC sys.sp_executesql @function_search_sql, N'@i_StoredProcName NVARCHAR(128)', @StoredProcName
1596 END
1597
1598 IF (SELECT COUNT(*) FROM #only_sql_handles) = 0
1599 BEGIN
1600 RAISERROR(N'No information for that stored procedure was found.', 0, 1) WITH NOWAIT;
1601 RETURN;
1602 END;
1603
1604END;
1605
1606
1607
1608IF ((@OnlyQueryHashes IS NOT NULL AND LEN(@OnlyQueryHashes) > 0)
1609 OR (@IgnoreQueryHashes IS NOT NULL AND LEN(@IgnoreQueryHashes) > 0))
1610 AND LEFT(@QueryFilter, 3) IN ('pro', 'fun')
1611BEGIN
1612 RAISERROR('You cannot limit by query hash and filter by stored procedure', 16, 1);
1613 RETURN;
1614END;
1615
1616/* If the user is attempting to limit by query hash, set up the
1617 #only_query_hashes temp table. This will be used to narrow down
1618 results.
1619
1620 Just a reminder: Using @OnlyQueryHashes will ignore stored
1621 procedures and triggers.
1622 */
1623IF @OnlyQueryHashes IS NOT NULL
1624 AND LEN(@OnlyQueryHashes) > 0
1625BEGIN
1626 RAISERROR(N'Setting up filter for Query Hashes', 0, 1) WITH NOWAIT;
1627 SET @individual = '';
1628
1629 WHILE LEN(@OnlyQueryHashes) > 0
1630 BEGIN
1631 IF PATINDEX('%,%', @OnlyQueryHashes) > 0
1632 BEGIN
1633 SET @individual = SUBSTRING(@OnlyQueryHashes, 0, PATINDEX('%,%',@OnlyQueryHashes)) ;
1634
1635 INSERT INTO #only_query_hashes
1636 SELECT CAST('' AS XML).value('xs:hexBinary( substring(sql:variable("@individual"), sql:column("t.pos")) )', 'varbinary(max)')
1637 FROM (SELECT CASE SUBSTRING(@individual, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos)
1638 OPTION (RECOMPILE) ;
1639
1640 --SELECT CAST(SUBSTRING(@individual, 1, 2) AS BINARY(8));
1641
1642 SET @OnlyQueryHashes = SUBSTRING(@OnlyQueryHashes, LEN(@individual + ',') + 1, LEN(@OnlyQueryHashes)) ;
1643 END;
1644 ELSE
1645 BEGIN
1646 SET @individual = @OnlyQueryHashes;
1647 SET @OnlyQueryHashes = NULL;
1648
1649 INSERT INTO #only_query_hashes
1650 SELECT CAST('' AS XML).value('xs:hexBinary( substring(sql:variable("@individual"), sql:column("t.pos")) )', 'varbinary(max)')
1651 FROM (SELECT CASE SUBSTRING(@individual, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos)
1652 OPTION (RECOMPILE) ;
1653
1654 --SELECT CAST(SUBSTRING(@individual, 1, 2) AS VARBINARY(MAX)) ;
1655 END;
1656 END;
1657END;
1658
1659/* If the user is setting up a list of query hashes to ignore, those
1660 values will be inserted into #ignore_query_hashes. This is used to
1661 exclude values from query results.
1662
1663 Just a reminder: Using @IgnoreQueryHashes will ignore stored
1664 procedures and triggers.
1665 */
1666IF @IgnoreQueryHashes IS NOT NULL
1667 AND LEN(@IgnoreQueryHashes) > 0
1668BEGIN
1669 RAISERROR(N'Setting up filter to ignore query hashes', 0, 1) WITH NOWAIT;
1670 SET @individual = '' ;
1671
1672 WHILE LEN(@IgnoreQueryHashes) > 0
1673 BEGIN
1674 IF PATINDEX('%,%', @IgnoreQueryHashes) > 0
1675 BEGIN
1676 SET @individual = SUBSTRING(@IgnoreQueryHashes, 0, PATINDEX('%,%',@IgnoreQueryHashes)) ;
1677
1678 INSERT INTO #ignore_query_hashes
1679 SELECT CAST('' AS XML).value('xs:hexBinary( substring(sql:variable("@individual"), sql:column("t.pos")) )', 'varbinary(max)')
1680 FROM (SELECT CASE SUBSTRING(@individual, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos)
1681 OPTION (RECOMPILE) ;
1682
1683 SET @IgnoreQueryHashes = SUBSTRING(@IgnoreQueryHashes, LEN(@individual + ',') + 1, LEN(@IgnoreQueryHashes)) ;
1684 END;
1685 ELSE
1686 BEGIN
1687 SET @individual = @IgnoreQueryHashes ;
1688 SET @IgnoreQueryHashes = NULL ;
1689
1690 INSERT INTO #ignore_query_hashes
1691 SELECT CAST('' AS XML).value('xs:hexBinary( substring(sql:variable("@individual"), sql:column("t.pos")) )', 'varbinary(max)')
1692 FROM (SELECT CASE SUBSTRING(@individual, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos)
1693 OPTION (RECOMPILE) ;
1694 END;
1695 END;
1696END;
1697
1698IF @ConfigurationDatabaseName IS NOT NULL
1699BEGIN
1700 RAISERROR(N'Reading values from Configuration Database', 0, 1) WITH NOWAIT;
1701 DECLARE @config_sql NVARCHAR(MAX) = N'INSERT INTO #configuration SELECT parameter_name, value FROM '
1702 + QUOTENAME(@ConfigurationDatabaseName)
1703 + '.' + QUOTENAME(@ConfigurationSchemaName)
1704 + '.' + QUOTENAME(@ConfigurationTableName)
1705 + ' ; ' ;
1706 EXEC(@config_sql);
1707END;
1708
1709RAISERROR(N'Setting up variables', 0, 1) WITH NOWAIT;
1710DECLARE @sql NVARCHAR(MAX) = N'',
1711 @insert_list NVARCHAR(MAX) = N'',
1712 @plans_triggers_select_list NVARCHAR(MAX) = N'',
1713 @body NVARCHAR(MAX) = N'',
1714 @body_where NVARCHAR(MAX) = N'WHERE 1 = 1 ' + @nl,
1715 @body_order NVARCHAR(MAX) = N'ORDER BY #sortable# DESC OPTION (RECOMPILE) ',
1716
1717 @q NVARCHAR(1) = N'''',
1718 @pv VARCHAR(20),
1719 @pos TINYINT,
1720 @v DECIMAL(6,2),
1721 @build INT;
1722
1723
1724RAISERROR (N'Determining SQL Server version.',0,1) WITH NOWAIT;
1725
1726INSERT INTO #checkversion (version)
1727SELECT CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(128))
1728OPTION (RECOMPILE);
1729
1730
1731SELECT @v = common_version ,
1732 @build = build
1733FROM #checkversion
1734OPTION (RECOMPILE);
1735
1736IF (@SortOrder IN ('memory grant', 'avg memory grant'))
1737 AND NOT EXISTS(SELECT * FROM sys.all_columns WHERE OBJECT_ID = OBJECT_ID('sys.dm_exec_query_stats') AND name = 'max_grant_kb')
1738BEGIN
1739 RAISERROR('Your version of SQL does not support sorting by memory grant or average memory grant. Please use another sort order.', 16, 1);
1740 RETURN;
1741END;
1742
1743IF (@SortOrder IN ('spills')
1744 AND NOT EXISTS(SELECT * FROM sys.all_columns WHERE OBJECT_ID = OBJECT_ID('sys.dm_exec_query_stats') AND name = 'max_spills'))
1745BEGIN
1746 RAISERROR('Your version of SQL does not support sorting by total spills. Please use another sort order.', 16, 1);
1747 RETURN;
1748END;
1749
1750IF (@SortOrder IN ('avg spills')
1751 AND NOT EXISTS(SELECT * FROM sys.all_columns WHERE OBJECT_ID = OBJECT_ID('sys.dm_exec_query_stats') AND name = 'total_spills'))
1752BEGIN
1753 RAISERROR('Your version of SQL does not support sorting by average spills. Please use another sort order.', 16, 1);
1754 RETURN;
1755END;
1756
1757IF ((LEFT(@QueryFilter, 3) = 'fun') AND (@v < 13))
1758BEGIN
1759 RAISERROR('Your version of SQL does not support filtering by functions. Please use another filter.', 16, 1);
1760 RETURN;
1761END;
1762
1763RAISERROR (N'Creating dynamic SQL based on SQL Server version.',0,1) WITH NOWAIT;
1764
1765SET @insert_list += N'
1766SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
1767INSERT INTO ##BlitzCacheProcs (SPID, QueryType, DatabaseName, AverageCPU, TotalCPU, AverageCPUPerMinute, PercentCPUByType, PercentDurationByType,
1768 PercentReadsByType, PercentExecutionsByType, AverageDuration, TotalDuration, AverageReads, TotalReads, ExecutionCount,
1769 ExecutionsPerMinute, TotalWrites, AverageWrites, PercentWritesByType, WritesPerMinute, PlanCreationTime,
1770 LastExecutionTime, StatementStartOffset, StatementEndOffset, MinReturnedRows, MaxReturnedRows, AverageReturnedRows, TotalReturnedRows,
1771 LastReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB, MaxUsedGrantKB, PercentMemoryGrantUsed, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills,
1772 QueryText, QueryPlan, TotalWorkerTimeForType, TotalElapsedTimeForType, TotalReadsForType,
1773 TotalExecutionCountForType, TotalWritesForType, SqlHandle, PlanHandle, QueryHash, QueryPlanHash,
1774 min_worker_time, max_worker_time, is_parallel, min_elapsed_time, max_elapsed_time, age_minutes, age_minutes_lifetime) ' ;
1775
1776SET @body += N'
1777FROM (SELECT TOP (@Top) x.*, xpa.*,
1778 CAST((CASE WHEN DATEDIFF(mi, cached_time, GETDATE()) > 0 AND execution_count > 1
1779 THEN DATEDIFF(mi, cached_time, GETDATE())
1780 ELSE NULL END) as MONEY) as age_minutes,
1781 CAST((CASE WHEN DATEDIFF(mi, cached_time, last_execution_time) > 0 AND execution_count > 1
1782 THEN DATEDIFF(mi, cached_time, last_execution_time)
1783 ELSE Null END) as MONEY) as age_minutes_lifetime
1784 FROM sys.#view# x
1785 CROSS APPLY (SELECT * FROM sys.dm_exec_plan_attributes(x.plan_handle) AS ixpa
1786 WHERE ixpa.attribute = ''dbid'') AS xpa ' + @nl ;
1787
1788SET @body += N' WHERE 1 = 1 ' + @nl ;
1789
1790
1791IF @IgnoreSystemDBs = 1
1792 BEGIN
1793 RAISERROR(N'Ignoring system databases by default', 0, 1) WITH NOWAIT;
1794 SET @body += N' AND COALESCE(DB_NAME(CAST(xpa.value AS INT)), '''') NOT IN (''master'', ''model'', ''msdb'', ''tempdb'', ''32767'') AND COALESCE(DB_NAME(CAST(xpa.value AS INT)), '''') NOT IN (SELECT name FROM sys.databases WHERE is_distributor = 1)' + @nl ;
1795 END;
1796
1797IF @DatabaseName IS NOT NULL OR @DatabaseName <> N''
1798 BEGIN
1799 RAISERROR(N'Filtering database name chosen', 0, 1) WITH NOWAIT;
1800 SET @body += N' AND CAST(xpa.value AS BIGINT) = DB_ID(N'
1801 + QUOTENAME(@DatabaseName, N'''')
1802 + N') ' + @nl;
1803 END;
1804
1805IF (SELECT COUNT(*) FROM #only_sql_handles) > 0
1806BEGIN
1807 RAISERROR(N'Including only chosen SQL Handles', 0, 1) WITH NOWAIT;
1808 SET @body += N' AND EXISTS(SELECT 1/0 FROM #only_sql_handles q WHERE q.sql_handle = x.sql_handle) ' + @nl ;
1809END;
1810
1811IF (SELECT COUNT(*) FROM #ignore_sql_handles) > 0
1812BEGIN
1813 RAISERROR(N'Including only chosen SQL Handles', 0, 1) WITH NOWAIT;
1814 SET @body += N' AND NOT EXISTS(SELECT 1/0 FROM #ignore_sql_handles q WHERE q.sql_handle = x.sql_handle) ' + @nl ;
1815END;
1816
1817IF (SELECT COUNT(*) FROM #only_query_hashes) > 0
1818 AND (SELECT COUNT(*) FROM #ignore_query_hashes) = 0
1819 AND (SELECT COUNT(*) FROM #only_sql_handles) = 0
1820 AND (SELECT COUNT(*) FROM #ignore_sql_handles) = 0
1821BEGIN
1822 RAISERROR(N'Including only chosen Query Hashes', 0, 1) WITH NOWAIT;
1823 SET @body += N' AND EXISTS(SELECT 1/0 FROM #only_query_hashes q WHERE q.query_hash = x.query_hash) ' + @nl ;
1824END;
1825
1826/* filtering for query hashes */
1827IF (SELECT COUNT(*) FROM #ignore_query_hashes) > 0
1828 AND (SELECT COUNT(*) FROM #only_query_hashes) = 0
1829BEGIN
1830 RAISERROR(N'Excluding chosen Query Hashes', 0, 1) WITH NOWAIT;
1831 SET @body += N' AND NOT EXISTS(SELECT 1/0 FROM #ignore_query_hashes iq WHERE iq.query_hash = x.query_hash) ' + @nl ;
1832END;
1833/* end filtering for query hashes */
1834
1835
1836IF @DurationFilter IS NOT NULL
1837 BEGIN
1838 RAISERROR(N'Setting duration filter', 0, 1) WITH NOWAIT;
1839 SET @body += N' AND (total_elapsed_time / 1000.0) / execution_count > @min_duration ' + @nl ;
1840 END;
1841
1842IF @MinutesBack IS NOT NULL
1843 BEGIN
1844 RAISERROR(N'Setting minutes back filter', 0, 1) WITH NOWAIT;
1845 SET @body += N' AND x.last_execution_time >= DATEADD(MINUTE, @min_back, GETDATE()) ' + @nl ;
1846 END;
1847
1848IF @SlowlySearchPlansFor IS NOT NULL
1849 BEGIN
1850 RAISERROR(N'Setting string search for @SlowlySearchPlansFor, so remember, this is gonna be slow', 0, 1) WITH NOWAIT;
1851 SET @SlowlySearchPlansFor = REPLACE((REPLACE((REPLACE((REPLACE((@SlowlySearchPlansFor), N'[', N'_')), N']', N'_')), N'^', N'_')), N'''', N'''''');
1852 SET @body_where += N' AND CAST(qp.query_plan AS NVARCHAR(MAX)) LIKE ''%' + @SlowlySearchPlansFor + '%'' ' + @nl;
1853 END
1854
1855
1856/* Apply the sort order here to only grab relevant plans.
1857 This should make it faster to process since we'll be pulling back fewer
1858 plans for processing.
1859 */
1860RAISERROR(N'Applying chosen sort order', 0, 1) WITH NOWAIT;
1861SELECT @body += N' ORDER BY ' +
1862 CASE @SortOrder WHEN N'cpu' THEN N'total_worker_time'
1863 WHEN N'reads' THEN N'total_logical_reads'
1864 WHEN N'writes' THEN N'total_logical_writes'
1865 WHEN N'duration' THEN N'total_elapsed_time'
1866 WHEN N'executions' THEN N'execution_count'
1867 WHEN N'compiles' THEN N'cached_time'
1868 WHEN N'memory grant' THEN N'max_grant_kb'
1869 WHEN N'spills' THEN N'max_spills'
1870 /* And now the averages */
1871 WHEN N'avg cpu' THEN N'total_worker_time / execution_count'
1872 WHEN N'avg reads' THEN N'total_logical_reads / execution_count'
1873 WHEN N'avg writes' THEN N'total_logical_writes / execution_count'
1874 WHEN N'avg duration' THEN N'total_elapsed_time / execution_count'
1875 WHEN N'avg memory grant' THEN N'CASE WHEN max_grant_kb = 0 THEN 0 ELSE max_grant_kb / execution_count END'
1876 WHEN N'avg spills' THEN N'CASE WHEN total_spills = 0 THEN 0 ELSE total_spills / execution_count END'
1877 WHEN N'avg executions' THEN 'CASE WHEN execution_count = 0 THEN 0
1878 WHEN COALESCE(CAST((CASE WHEN DATEDIFF(mi, cached_time, GETDATE()) > 0 AND execution_count > 1
1879 THEN DATEDIFF(mi, cached_time, GETDATE())
1880 ELSE NULL END) as MONEY), CAST((CASE WHEN DATEDIFF(mi, cached_time, last_execution_time) > 0 AND execution_count > 1
1881 THEN DATEDIFF(mi, cached_time, last_execution_time)
1882 ELSE Null END) as MONEY), 0) = 0 THEN 0
1883 ELSE CAST((1.00 * execution_count / COALESCE(CAST((CASE WHEN DATEDIFF(mi, cached_time, GETDATE()) > 0 AND execution_count > 1
1884 THEN DATEDIFF(mi, cached_time, GETDATE())
1885 ELSE NULL END) as MONEY), CAST((CASE WHEN DATEDIFF(mi, cached_time, last_execution_time) > 0 AND execution_count > 1
1886 THEN DATEDIFF(mi, cached_time, last_execution_time)
1887 ELSE Null END) as MONEY))) AS money)
1888 END '
1889 END + N' DESC ' + @nl ;
1890
1891
1892
1893SET @body += N') AS qs
1894 CROSS JOIN(SELECT SUM(execution_count) AS t_TotalExecs,
1895 SUM(CAST(total_elapsed_time AS BIGINT) / 1000.0) AS t_TotalElapsed,
1896 SUM(CAST(total_worker_time AS BIGINT) / 1000.0) AS t_TotalWorker,
1897 SUM(CAST(total_logical_reads AS BIGINT)) AS t_TotalReads,
1898 SUM(CAST(total_logical_writes AS BIGINT)) AS t_TotalWrites
1899 FROM sys.#view#) AS t
1900 CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) AS pa
1901 CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
1902 CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp ' + @nl ;
1903
1904SET @body_where += N' AND pa.attribute = ' + QUOTENAME('dbid', @q ) + @nl ;
1905
1906
1907IF @NoobSaibot = 1
1908BEGIN
1909 SET @body_where += N' AND qp.query_plan.exist(''declare namespace p="http://schemas.microsoft.com/sqlserver/2004/07/showplan";//p:StmtSimple//p:MissingIndex'') = 1' + @nl ;
1910END
1911
1912SET @plans_triggers_select_list += N'
1913SELECT TOP (@Top)
1914 @@SPID ,
1915 ''Procedure or Function: ''
1916 + QUOTENAME(COALESCE(OBJECT_SCHEMA_NAME(qs.object_id, qs.database_id),''''))
1917 + ''.''
1918 + QUOTENAME(COALESCE(OBJECT_NAME(qs.object_id, qs.database_id),'''')) AS QueryType,
1919 COALESCE(DB_NAME(database_id), CAST(pa.value AS sysname), N''-- N/A --'') AS DatabaseName,
1920 (total_worker_time / 1000.0) / execution_count AS AvgCPU ,
1921 (total_worker_time / 1000.0) AS TotalCPU ,
1922 CASE WHEN total_worker_time = 0 THEN 0
1923 WHEN COALESCE(age_minutes, DATEDIFF(mi, qs.cached_time, qs.last_execution_time), 0) = 0 THEN 0
1924 ELSE CAST((total_worker_time / 1000.0) / COALESCE(age_minutes, DATEDIFF(mi, qs.cached_time, qs.last_execution_time)) AS MONEY)
1925 END AS AverageCPUPerMinute ,
1926 CASE WHEN t.t_TotalWorker = 0 THEN 0
1927 ELSE CAST(ROUND(100.00 * (total_worker_time / 1000.0) / t.t_TotalWorker, 2) AS MONEY)
1928 END AS PercentCPUByType,
1929 CASE WHEN t.t_TotalElapsed = 0 THEN 0
1930 ELSE CAST(ROUND(100.00 * (total_elapsed_time / 1000.0) / t.t_TotalElapsed, 2) AS MONEY)
1931 END AS PercentDurationByType,
1932 CASE WHEN t.t_TotalReads = 0 THEN 0
1933 ELSE CAST(ROUND(100.00 * total_logical_reads / t.t_TotalReads, 2) AS MONEY)
1934 END AS PercentReadsByType,
1935 CASE WHEN t.t_TotalExecs = 0 THEN 0
1936 ELSE CAST(ROUND(100.00 * execution_count / t.t_TotalExecs, 2) AS MONEY)
1937 END AS PercentExecutionsByType,
1938 (total_elapsed_time / 1000.0) / execution_count AS AvgDuration ,
1939 (total_elapsed_time / 1000.0) AS TotalDuration ,
1940 total_logical_reads / execution_count AS AvgReads ,
1941 total_logical_reads AS TotalReads ,
1942 execution_count AS ExecutionCount ,
1943 CASE WHEN execution_count = 0 THEN 0
1944 WHEN COALESCE(age_minutes, DATEDIFF(mi, qs.cached_time, qs.last_execution_time), 0) = 0 THEN 0
1945 ELSE CAST((1.00 * execution_count / COALESCE(age_minutes, DATEDIFF(mi, qs.cached_time, qs.last_execution_time))) AS money)
1946 END AS ExecutionsPerMinute ,
1947 total_logical_writes AS TotalWrites ,
1948 total_logical_writes / execution_count AS AverageWrites ,
1949 CASE WHEN t.t_TotalWrites = 0 THEN 0
1950 ELSE CAST(ROUND(100.00 * total_logical_writes / t.t_TotalWrites, 2) AS MONEY)
1951 END AS PercentWritesByType,
1952 CASE WHEN total_logical_writes = 0 THEN 0
1953 WHEN COALESCE(age_minutes, DATEDIFF(mi, qs.cached_time, qs.last_execution_time), 0) = 0 THEN 0
1954 ELSE CAST((1.00 * total_logical_writes / COALESCE(age_minutes, DATEDIFF(mi, qs.cached_time, qs.last_execution_time), 0)) AS money)
1955 END AS WritesPerMinute,
1956 qs.cached_time AS PlanCreationTime,
1957 qs.last_execution_time AS LastExecutionTime,
1958 NULL AS StatementStartOffset,
1959 NULL AS StatementEndOffset,
1960 NULL AS MinReturnedRows,
1961 NULL AS MaxReturnedRows,
1962 NULL AS AvgReturnedRows,
1963 NULL AS TotalReturnedRows,
1964 NULL AS LastReturnedRows,
1965 NULL AS MinGrantKB,
1966 NULL AS MaxGrantKB,
1967 NULL AS MinUsedGrantKB,
1968 NULL AS MaxUsedGrantKB,
1969 NULL AS PercentMemoryGrantUsed,
1970 NULL AS AvgMaxMemoryGrant,';
1971
1972 IF @v >=15 OR (@v = 14 AND @build >= 3015) OR (@v = 13 AND @build >= 5026)
1973 BEGIN
1974 RAISERROR(N'Getting spill information for newer versions of SQL', 0, 1) WITH NOWAIT;
1975 SET @plans_triggers_select_list += N'
1976 min_spills AS MinSpills,
1977 max_spills AS MaxSpills,
1978 total_spills AS TotalSpills,
1979 CAST(ISNULL(NULLIF(( total_spills * 1. ), 0) / NULLIF(execution_count, 0), 0) AS MONEY) AS AvgSpills, ';
1980 END;
1981 ELSE
1982 BEGIN
1983 RAISERROR(N'Substituting NULLs for spill columns in older versions of SQL', 0, 1) WITH NOWAIT;
1984 SET @plans_triggers_select_list += N'
1985 NULL AS MinSpills,
1986 NULL AS MaxSpills,
1987 NULL AS TotalSpills,
1988 NULL AS AvgSpills, ' ;
1989 END;
1990
1991 SET @plans_triggers_select_list +=
1992 N'st.text AS QueryText ,
1993 query_plan AS QueryPlan,
1994 t.t_TotalWorker,
1995 t.t_TotalElapsed,
1996 t.t_TotalReads,
1997 t.t_TotalExecs,
1998 t.t_TotalWrites,
1999 qs.sql_handle AS SqlHandle,
2000 qs.plan_handle AS PlanHandle,
2001 NULL AS QueryHash,
2002 NULL AS QueryPlanHash,
2003 qs.min_worker_time / 1000.0,
2004 qs.max_worker_time / 1000.0,
2005 CASE WHEN qp.query_plan.value(''declare namespace p="http://schemas.microsoft.com/sqlserver/2004/07/showplan";max(//p:RelOp/@Parallel)'', ''float'') > 0 THEN 1 ELSE 0 END,
2006 qs.min_elapsed_time / 1000.0,
2007 qs.max_elapsed_time / 1000.0,
2008 age_minutes,
2009 age_minutes_lifetime ';
2010
2011
2012IF LEFT(@QueryFilter, 3) IN ('all', 'sta')
2013BEGIN
2014 SET @sql += @insert_list;
2015
2016 SET @sql += N'
2017 SELECT TOP (@Top)
2018 @@SPID ,
2019 ''Statement'' AS QueryType,
2020 COALESCE(DB_NAME(CAST(pa.value AS INT)), N''-- N/A --'') AS DatabaseName,
2021 (total_worker_time / 1000.0) / execution_count AS AvgCPU ,
2022 (total_worker_time / 1000.0) AS TotalCPU ,
2023 CASE WHEN total_worker_time = 0 THEN 0
2024 WHEN COALESCE(age_minutes, DATEDIFF(mi, qs.creation_time, qs.last_execution_time), 0) = 0 THEN 0
2025 ELSE CAST((total_worker_time / 1000.0) / COALESCE(age_minutes, DATEDIFF(mi, qs.creation_time, qs.last_execution_time)) AS MONEY)
2026 END AS AverageCPUPerMinute ,
2027 CASE WHEN t.t_TotalWorker = 0 THEN 0
2028 ELSE CAST(ROUND(100.00 * total_worker_time / t.t_TotalWorker, 2) AS MONEY)
2029 END AS PercentCPUByType,
2030 CASE WHEN t.t_TotalElapsed = 0 THEN 0
2031 ELSE CAST(ROUND(100.00 * total_elapsed_time / t.t_TotalElapsed, 2) AS MONEY)
2032 END AS PercentDurationByType,
2033 CASE WHEN t.t_TotalReads = 0 THEN 0
2034 ELSE CAST(ROUND(100.00 * total_logical_reads / t.t_TotalReads, 2) AS MONEY)
2035 END AS PercentReadsByType,
2036 CAST(ROUND(100.00 * execution_count / t.t_TotalExecs, 2) AS MONEY) AS PercentExecutionsByType,
2037 (total_elapsed_time / 1000.0) / execution_count AS AvgDuration ,
2038 (total_elapsed_time / 1000.0) AS TotalDuration ,
2039 total_logical_reads / execution_count AS AvgReads ,
2040 total_logical_reads AS TotalReads ,
2041 execution_count AS ExecutionCount ,
2042 CASE WHEN execution_count = 0 THEN 0
2043 WHEN COALESCE(age_minutes, DATEDIFF(mi, qs.creation_time, qs.last_execution_time), 0) = 0 THEN 0
2044 ELSE CAST((1.00 * execution_count / COALESCE(age_minutes, DATEDIFF(mi, qs.creation_time, qs.last_execution_time))) AS money)
2045 END AS ExecutionsPerMinute ,
2046 total_logical_writes AS TotalWrites ,
2047 total_logical_writes / execution_count AS AverageWrites ,
2048 CASE WHEN t.t_TotalWrites = 0 THEN 0
2049 ELSE CAST(ROUND(100.00 * total_logical_writes / t.t_TotalWrites, 2) AS MONEY)
2050 END AS PercentWritesByType,
2051 CASE WHEN total_logical_writes = 0 THEN 0
2052 WHEN COALESCE(age_minutes, DATEDIFF(mi, qs.creation_time, qs.last_execution_time), 0) = 0 THEN 0
2053 ELSE CAST((1.00 * total_logical_writes / COALESCE(age_minutes, DATEDIFF(mi, qs.creation_time, qs.last_execution_time), 0)) AS money)
2054 END AS WritesPerMinute,
2055 qs.creation_time AS PlanCreationTime,
2056 qs.last_execution_time AS LastExecutionTime,
2057 qs.statement_start_offset AS StatementStartOffset,
2058 qs.statement_end_offset AS StatementEndOffset, ';
2059
2060 IF (@v >= 11) OR (@v >= 10.5 AND @build >= 2500)
2061 BEGIN
2062 RAISERROR(N'Adding additional info columns for newer versions of SQL', 0, 1) WITH NOWAIT;
2063 SET @sql += N'
2064 qs.min_rows AS MinReturnedRows,
2065 qs.max_rows AS MaxReturnedRows,
2066 CAST(qs.total_rows as MONEY) / execution_count AS AvgReturnedRows,
2067 qs.total_rows AS TotalReturnedRows,
2068 qs.last_rows AS LastReturnedRows, ' ;
2069 END;
2070 ELSE
2071 BEGIN
2072 RAISERROR(N'Substituting NULLs for more info columns in older versions of SQL', 0, 1) WITH NOWAIT;
2073 SET @sql += N'
2074 NULL AS MinReturnedRows,
2075 NULL AS MaxReturnedRows,
2076 NULL AS AvgReturnedRows,
2077 NULL AS TotalReturnedRows,
2078 NULL AS LastReturnedRows, ' ;
2079 END;
2080
2081 IF (@v = 11 AND @build >= 6020) OR (@v = 12 AND @build >= 5000) OR (@v = 13 AND @build >= 1601) OR (@v >= 14)
2082
2083 BEGIN
2084 RAISERROR(N'Getting memory grant information for newer versions of SQL', 0, 1) WITH NOWAIT;
2085 SET @sql += N'
2086 min_grant_kb AS MinGrantKB,
2087 max_grant_kb AS MaxGrantKB,
2088 min_used_grant_kb AS MinUsedGrantKB,
2089 max_used_grant_kb AS MaxUsedGrantKB,
2090 CAST(ISNULL(NULLIF(( max_used_grant_kb * 1.00 ), 0) / NULLIF(min_grant_kb, 0), 0) * 100. AS MONEY) AS PercentMemoryGrantUsed,
2091 CAST(ISNULL(NULLIF(( max_grant_kb * 1. ), 0) / NULLIF(execution_count, 0), 0) AS MONEY) AS AvgMaxMemoryGrant, ';
2092 END;
2093 ELSE
2094 BEGIN
2095 RAISERROR(N'Substituting NULLs for memory grant columns in older versions of SQL', 0, 1) WITH NOWAIT;
2096 SET @sql += N'
2097 NULL AS MinGrantKB,
2098 NULL AS MaxGrantKB,
2099 NULL AS MinUsedGrantKB,
2100 NULL AS MaxUsedGrantKB,
2101 NULL AS PercentMemoryGrantUsed,
2102 NULL AS AvgMaxMemoryGrant, ' ;
2103 END;
2104
2105 IF @v >=15 OR (@v = 14 AND @build >= 3015) OR (@v = 13 AND @build >= 5026)
2106 BEGIN
2107 RAISERROR(N'Getting spill information for newer versions of SQL', 0, 1) WITH NOWAIT;
2108 SET @sql += N'
2109 min_spills AS MinSpills,
2110 max_spills AS MaxSpills,
2111 total_spills AS TotalSpills,
2112 CAST(ISNULL(NULLIF(( total_spills * 1. ), 0) / NULLIF(execution_count, 0), 0) AS MONEY) AS AvgSpills,';
2113 END;
2114 ELSE
2115 BEGIN
2116 RAISERROR(N'Substituting NULLs for spill columns in older versions of SQL', 0, 1) WITH NOWAIT;
2117 SET @sql += N'
2118 NULL AS MinSpills,
2119 NULL AS MaxSpills,
2120 NULL AS TotalSpills,
2121 NULL AS AvgSpills, ' ;
2122 END;
2123
2124 SET @sql += N'
2125 SUBSTRING(st.text, ( qs.statement_start_offset / 2 ) + 1, ( ( CASE qs.statement_end_offset
2126 WHEN -1 THEN DATALENGTH(st.text)
2127 ELSE qs.statement_end_offset
2128 END - qs.statement_start_offset ) / 2 ) + 1) AS QueryText ,
2129 query_plan AS QueryPlan,
2130 t.t_TotalWorker,
2131 t.t_TotalElapsed,
2132 t.t_TotalReads,
2133 t.t_TotalExecs,
2134 t.t_TotalWrites,
2135 qs.sql_handle AS SqlHandle,
2136 qs.plan_handle AS PlanHandle,
2137 qs.query_hash AS QueryHash,
2138 qs.query_plan_hash AS QueryPlanHash,
2139 qs.min_worker_time / 1000.0,
2140 qs.max_worker_time / 1000.0,
2141 CASE WHEN qp.query_plan.value(''declare namespace p="http://schemas.microsoft.com/sqlserver/2004/07/showplan";max(//p:RelOp/@Parallel)'', ''float'') > 0 THEN 1 ELSE 0 END,
2142 qs.min_elapsed_time / 1000.0,
2143 qs.max_worker_time / 1000.0,
2144 age_minutes,
2145 age_minutes_lifetime ';
2146
2147 SET @sql += REPLACE(REPLACE(@body, '#view#', 'dm_exec_query_stats'), 'cached_time', 'creation_time') ;
2148
2149 SET @sql += REPLACE(@body_where, 'cached_time', 'creation_time') ;
2150
2151 SET @sql += @body_order + @nl + @nl + @nl;
2152
2153 IF @SortOrder = 'compiles'
2154 BEGIN
2155 RAISERROR(N'Sorting by compiles', 0, 1) WITH NOWAIT;
2156 SET @sql = REPLACE(@sql, '#sortable#', 'creation_time');
2157 END;
2158END;
2159
2160
2161IF (@QueryFilter = 'all'
2162 AND (SELECT COUNT(*) FROM #only_query_hashes) = 0
2163 AND (SELECT COUNT(*) FROM #ignore_query_hashes) = 0)
2164 AND (@SortOrder NOT IN ('memory grant', 'avg memory grant'))
2165 OR (LEFT(@QueryFilter, 3) = 'pro')
2166BEGIN
2167 SET @sql += @insert_list;
2168 SET @sql += REPLACE(@plans_triggers_select_list, '#query_type#', 'Stored Procedure') ;
2169
2170 SET @sql += REPLACE(@body, '#view#', 'dm_exec_procedure_stats') ;
2171 SET @sql += @body_where ;
2172
2173 IF @IgnoreSystemDBs = 1
2174 SET @sql += N' AND COALESCE(DB_NAME(database_id), CAST(pa.value AS sysname), '''') NOT IN (''master'', ''model'', ''msdb'', ''tempdb'', ''32767'') AND COALESCE(DB_NAME(database_id), CAST(pa.value AS sysname), '''') NOT IN (SELECT name FROM sys.databases WHERE is_distributor = 1)' + @nl ;
2175
2176 SET @sql += @body_order + @nl + @nl + @nl ;
2177END;
2178
2179IF (@v >= 13
2180 AND @QueryFilter = 'all'
2181 AND (SELECT COUNT(*) FROM #only_query_hashes) = 0
2182 AND (SELECT COUNT(*) FROM #ignore_query_hashes) = 0)
2183 AND (@SortOrder NOT IN ('memory grant', 'avg memory grant'))
2184 AND (@SortOrder NOT IN ('spills', 'avg spills'))
2185 OR (LEFT(@QueryFilter, 3) = 'fun')
2186BEGIN
2187 SET @sql += @insert_list;
2188 SET @sql += REPLACE(REPLACE(@plans_triggers_select_list, '#query_type#', 'Function')
2189 , N'
2190 min_spills AS MinSpills,
2191 max_spills AS MaxSpills,
2192 total_spills AS TotalSpills,
2193 CAST(ISNULL(NULLIF(( total_spills * 1. ), 0) / NULLIF(execution_count, 0), 0) AS MONEY) AS AvgSpills, ',
2194 N'
2195 NULL AS MinSpills,
2196 NULL AS MaxSpills,
2197 NULL AS TotalSpills,
2198 NULL AS AvgSpills, ') ;
2199
2200 SET @sql += REPLACE(@body, '#view#', 'dm_exec_function_stats') ;
2201 SET @sql += @body_where ;
2202
2203 IF @IgnoreSystemDBs = 1
2204 SET @sql += N' AND COALESCE(DB_NAME(database_id), CAST(pa.value AS sysname), '''') NOT IN (''master'', ''model'', ''msdb'', ''tempdb'', ''32767'') AND COALESCE(DB_NAME(database_id), CAST(pa.value AS sysname), '''') NOT IN (SELECT name FROM sys.databases WHERE is_distributor = 1)' + @nl ;
2205
2206 SET @sql += @body_order + @nl + @nl + @nl ;
2207END;
2208
2209/*******************************************************************************
2210 *
2211 * Because the trigger execution count in SQL Server 2008R2 and earlier is not
2212 * correct, we ignore triggers for these versions of SQL Server. If you'd like
2213 * to include trigger numbers, just know that the ExecutionCount,
2214 * PercentExecutions, and ExecutionsPerMinute are wildly inaccurate for
2215 * triggers on these versions of SQL Server.
2216 *
2217 * This is why we can't have nice things.
2218 *
2219 ******************************************************************************/
2220IF (@UseTriggersAnyway = 1 OR @v >= 11)
2221 AND (SELECT COUNT(*) FROM #only_query_hashes) = 0
2222 AND (SELECT COUNT(*) FROM #ignore_query_hashes) = 0
2223 AND (@QueryFilter = 'all')
2224 AND (@SortOrder NOT IN ('memory grant', 'avg memory grant'))
2225BEGIN
2226 RAISERROR (N'Adding SQL to collect trigger stats.',0,1) WITH NOWAIT;
2227
2228 /* Trigger level information from the plan cache */
2229 SET @sql += @insert_list ;
2230
2231 SET @sql += REPLACE(@plans_triggers_select_list, '#query_type#', 'Trigger') ;
2232
2233 SET @sql += REPLACE(@body, '#view#', 'dm_exec_trigger_stats') ;
2234
2235 SET @sql += @body_where ;
2236
2237 IF @IgnoreSystemDBs = 1
2238 SET @sql += N' AND COALESCE(DB_NAME(database_id), CAST(pa.value AS sysname), '''') NOT IN (''master'', ''model'', ''msdb'', ''tempdb'', ''32767'') AND COALESCE(DB_NAME(database_id), CAST(pa.value AS sysname), '''') NOT IN (SELECT name FROM sys.databases WHERE is_distributor = 1)' + @nl ;
2239
2240 SET @sql += @body_order + @nl + @nl + @nl ;
2241END;
2242
2243DECLARE @sort NVARCHAR(MAX);
2244
2245SELECT @sort = CASE @SortOrder WHEN N'cpu' THEN N'total_worker_time'
2246 WHEN N'reads' THEN N'total_logical_reads'
2247 WHEN N'writes' THEN N'total_logical_writes'
2248 WHEN N'duration' THEN N'total_elapsed_time'
2249 WHEN N'executions' THEN N'execution_count'
2250 WHEN N'compiles' THEN N'cached_time'
2251 WHEN N'memory grant' THEN N'max_grant_kb'
2252 WHEN N'spills' THEN N'max_spills'
2253 /* And now the averages */
2254 WHEN N'avg cpu' THEN N'total_worker_time / execution_count'
2255 WHEN N'avg reads' THEN N'total_logical_reads / execution_count'
2256 WHEN N'avg writes' THEN N'total_logical_writes / execution_count'
2257 WHEN N'avg duration' THEN N'total_elapsed_time / execution_count'
2258 WHEN N'avg memory grant' THEN N'CASE WHEN max_grant_kb = 0 THEN 0 ELSE max_grant_kb / execution_count END'
2259 WHEN N'avg spills' THEN N'CASE WHEN total_spills = 0 THEN 0 ELSE total_spills / execution_count END'
2260 WHEN N'avg executions' THEN N'CASE WHEN execution_count = 0 THEN 0
2261 WHEN COALESCE(age_minutes, age_minutes_lifetime, 0) = 0 THEN 0
2262 ELSE CAST((1.00 * execution_count / COALESCE(age_minutes, age_minutes_lifetime)) AS money)
2263 END'
2264 END ;
2265
2266SELECT @sql = REPLACE(@sql, '#sortable#', @sort);
2267
2268SET @sql += N'
2269SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
2270INSERT INTO #p (SqlHandle, TotalCPU, TotalReads, TotalDuration, TotalWrites, ExecutionCount)
2271SELECT SqlHandle,
2272 TotalCPU,
2273 TotalReads,
2274 TotalDuration,
2275 TotalWrites,
2276 ExecutionCount
2277FROM (SELECT SqlHandle,
2278 TotalCPU,
2279 TotalReads,
2280 TotalDuration,
2281 TotalWrites,
2282 ExecutionCount,
2283 ROW_NUMBER() OVER (PARTITION BY SqlHandle ORDER BY #sortable# DESC) AS rn
2284 FROM ##BlitzCacheProcs
2285 WHERE SPID = @@SPID) AS x
2286WHERE x.rn = 1
2287OPTION (RECOMPILE);
2288
2289WITH d AS (
2290SELECT SPID,
2291 ROW_NUMBER() OVER (PARTITION BY SqlHandle, QueryHash ORDER BY #sortable# DESC) AS rn
2292FROM ##BlitzCacheProcs
2293WHERE SPID = @@SPID
2294)
2295DELETE d
2296WHERE d.rn > 1
2297AND SPID = @@SPID
2298OPTION (RECOMPILE);
2299';
2300
2301SELECT @sort = CASE @SortOrder WHEN N'cpu' THEN N'TotalCPU'
2302 WHEN N'reads' THEN N'TotalReads'
2303 WHEN N'writes' THEN N'TotalWrites'
2304 WHEN N'duration' THEN N'TotalDuration'
2305 WHEN N'executions' THEN N'ExecutionCount'
2306 WHEN N'compiles' THEN N'PlanCreationTime'
2307 WHEN N'memory grant' THEN N'MaxGrantKB'
2308 WHEN N'spills' THEN N'MaxSpills'
2309 /* And now the averages */
2310 WHEN N'avg cpu' THEN N'TotalCPU / ExecutionCount'
2311 WHEN N'avg reads' THEN N'TotalReads / ExecutionCount'
2312 WHEN N'avg writes' THEN N'TotalWrites / ExecutionCount'
2313 WHEN N'avg duration' THEN N'TotalDuration / ExecutionCount'
2314 WHEN N'avg memory grant' THEN N'AvgMaxMemoryGrant'
2315 WHEN N'avg spills' THEN N'AvgSpills'
2316 WHEN N'avg executions' THEN N'CASE WHEN ExecutionCount = 0 THEN 0
2317 WHEN COALESCE(age_minutes, age_minutes_lifetime, 0) = 0 THEN 0
2318 ELSE CAST((1.00 * ExecutionCount / COALESCE(age_minutes, age_minutes_lifetime)) AS money)
2319 END'
2320 END ;
2321
2322SELECT @sql = REPLACE(@sql, '#sortable#', @sort);
2323
2324
2325IF @Debug = 1
2326 BEGIN
2327 PRINT SUBSTRING(@sql, 0, 4000);
2328 PRINT SUBSTRING(@sql, 4000, 8000);
2329 PRINT SUBSTRING(@sql, 8000, 12000);
2330 PRINT SUBSTRING(@sql, 12000, 16000);
2331 PRINT SUBSTRING(@sql, 16000, 20000);
2332 PRINT SUBSTRING(@sql, 20000, 24000);
2333 PRINT SUBSTRING(@sql, 24000, 28000);
2334 PRINT SUBSTRING(@sql, 28000, 32000);
2335 PRINT SUBSTRING(@sql, 32000, 36000);
2336 PRINT SUBSTRING(@sql, 36000, 40000);
2337 END;
2338
2339IF @Reanalyze = 0
2340BEGIN
2341 RAISERROR('Collecting execution plan information.', 0, 1) WITH NOWAIT;
2342
2343 EXEC sp_executesql @sql, N'@Top INT, @min_duration INT, @min_back INT', @Top, @DurationFilter_i, @MinutesBack;
2344END;
2345
2346IF @SkipAnalysis = 1
2347 BEGIN
2348 RAISERROR(N'Skipping analysis, going to results', 0, 1) WITH NOWAIT;
2349 GOTO Results ;
2350 END;
2351
2352
2353/* Update ##BlitzCacheProcs to get Stored Proc info
2354 * This should get totals for all statements in a Stored Proc
2355 */
2356RAISERROR(N'Attempting to aggregate stored proc info from separate statements', 0, 1) WITH NOWAIT;
2357;WITH agg AS (
2358 SELECT
2359 b.SqlHandle,
2360 SUM(b.MinReturnedRows) AS MinReturnedRows,
2361 SUM(b.MaxReturnedRows) AS MaxReturnedRows,
2362 SUM(b.AverageReturnedRows) AS AverageReturnedRows,
2363 SUM(b.TotalReturnedRows) AS TotalReturnedRows,
2364 SUM(b.LastReturnedRows) AS LastReturnedRows,
2365 SUM(b.MinGrantKB) AS MinGrantKB,
2366 SUM(b.MaxGrantKB) AS MaxGrantKB,
2367 SUM(b.MinUsedGrantKB) AS MinUsedGrantKB,
2368 SUM(b.MaxUsedGrantKB) AS MaxUsedGrantKB,
2369 SUM(b.MinSpills) AS MinSpills,
2370 SUM(b.MaxSpills) AS MaxSpills,
2371 SUM(b.TotalSpills) AS TotalSpills
2372 FROM ##BlitzCacheProcs b
2373 WHERE b.SPID = @@SPID
2374 AND b.QueryHash IS NOT NULL
2375 GROUP BY b.SqlHandle
2376)
2377UPDATE b
2378 SET
2379 b.MinReturnedRows = b2.MinReturnedRows,
2380 b.MaxReturnedRows = b2.MaxReturnedRows,
2381 b.AverageReturnedRows = b2.AverageReturnedRows,
2382 b.TotalReturnedRows = b2.TotalReturnedRows,
2383 b.LastReturnedRows = b2.LastReturnedRows,
2384 b.MinGrantKB = b2.MinGrantKB,
2385 b.MaxGrantKB = b2.MaxGrantKB,
2386 b.MinUsedGrantKB = b2.MinUsedGrantKB,
2387 b.MaxUsedGrantKB = b2.MaxUsedGrantKB,
2388 b.MinSpills = b2.MinSpills,
2389 b.MaxSpills = b2.MaxSpills,
2390 b.TotalSpills = b2.TotalSpills
2391FROM ##BlitzCacheProcs b
2392JOIN agg b2
2393ON b2.SqlHandle = b.SqlHandle
2394WHERE b.QueryHash IS NULL
2395AND b.SPID = @@SPID
2396OPTION (RECOMPILE) ;
2397
2398/* Compute the total CPU, etc across our active set of the plan cache.
2399 * Yes, there's a flaw - this doesn't include anything outside of our @Top
2400 * metric.
2401 */
2402RAISERROR('Computing CPU, duration, read, and write metrics', 0, 1) WITH NOWAIT;
2403DECLARE @total_duration MONEY,
2404 @total_cpu MONEY,
2405 @total_reads MONEY,
2406 @total_writes MONEY,
2407 @total_execution_count MONEY;
2408
2409SELECT @total_cpu = SUM(TotalCPU),
2410 @total_duration = SUM(TotalDuration),
2411 @total_reads = SUM(TotalReads),
2412 @total_writes = SUM(TotalWrites),
2413 @total_execution_count = SUM(ExecutionCount)
2414FROM #p
2415OPTION (RECOMPILE) ;
2416
2417DECLARE @cr NVARCHAR(1) = NCHAR(13);
2418DECLARE @lf NVARCHAR(1) = NCHAR(10);
2419DECLARE @tab NVARCHAR(1) = NCHAR(9);
2420
2421/* Update CPU percentage for stored procedures */
2422RAISERROR(N'Update CPU percentage for stored procedures', 0, 1) WITH NOWAIT;
2423UPDATE ##BlitzCacheProcs
2424SET PercentCPU = y.PercentCPU,
2425 PercentDuration = y.PercentDuration,
2426 PercentReads = y.PercentReads,
2427 PercentWrites = y.PercentWrites,
2428 PercentExecutions = y.PercentExecutions,
2429 ExecutionsPerMinute = y.ExecutionsPerMinute,
2430 /* Strip newlines and tabs. Tabs are replaced with multiple spaces
2431 so that the later whitespace trim will completely eliminate them
2432 */
2433 QueryText = REPLACE(REPLACE(REPLACE(QueryText, @cr, ' '), @lf, ' '), @tab, ' ')
2434FROM (
2435 SELECT PlanHandle,
2436 CASE @total_cpu WHEN 0 THEN 0
2437 ELSE CAST((100. * TotalCPU) / @total_cpu AS MONEY) END AS PercentCPU,
2438 CASE @total_duration WHEN 0 THEN 0
2439 ELSE CAST((100. * TotalDuration) / @total_duration AS MONEY) END AS PercentDuration,
2440 CASE @total_reads WHEN 0 THEN 0
2441 ELSE CAST((100. * TotalReads) / @total_reads AS MONEY) END AS PercentReads,
2442 CASE @total_writes WHEN 0 THEN 0
2443 ELSE CAST((100. * TotalWrites) / @total_writes AS MONEY) END AS PercentWrites,
2444 CASE @total_execution_count WHEN 0 THEN 0
2445 ELSE CAST((100. * ExecutionCount) / @total_execution_count AS MONEY) END AS PercentExecutions,
2446 CASE DATEDIFF(mi, PlanCreationTime, LastExecutionTime)
2447 WHEN 0 THEN 0
2448 ELSE CAST((1.00 * ExecutionCount / DATEDIFF(mi, PlanCreationTime, LastExecutionTime)) AS MONEY)
2449 END AS ExecutionsPerMinute
2450 FROM (
2451 SELECT PlanHandle,
2452 TotalCPU,
2453 TotalDuration,
2454 TotalReads,
2455 TotalWrites,
2456 ExecutionCount,
2457 PlanCreationTime,
2458 LastExecutionTime
2459 FROM ##BlitzCacheProcs
2460 WHERE PlanHandle IS NOT NULL
2461 AND SPID = @@SPID
2462 GROUP BY PlanHandle,
2463 TotalCPU,
2464 TotalDuration,
2465 TotalReads,
2466 TotalWrites,
2467 ExecutionCount,
2468 PlanCreationTime,
2469 LastExecutionTime
2470 ) AS x
2471) AS y
2472WHERE ##BlitzCacheProcs.PlanHandle = y.PlanHandle
2473 AND ##BlitzCacheProcs.PlanHandle IS NOT NULL
2474 AND ##BlitzCacheProcs.SPID = @@SPID
2475OPTION (RECOMPILE) ;
2476
2477
2478RAISERROR(N'Gather percentage information from grouped results', 0, 1) WITH NOWAIT;
2479UPDATE ##BlitzCacheProcs
2480SET PercentCPU = y.PercentCPU,
2481 PercentDuration = y.PercentDuration,
2482 PercentReads = y.PercentReads,
2483 PercentWrites = y.PercentWrites,
2484 PercentExecutions = y.PercentExecutions,
2485 ExecutionsPerMinute = y.ExecutionsPerMinute,
2486 /* Strip newlines and tabs. Tabs are replaced with multiple spaces
2487 so that the later whitespace trim will completely eliminate them
2488 */
2489 QueryText = REPLACE(REPLACE(REPLACE(QueryText, @cr, ' '), @lf, ' '), @tab, ' ')
2490FROM (
2491 SELECT DatabaseName,
2492 SqlHandle,
2493 QueryHash,
2494 CASE @total_cpu WHEN 0 THEN 0
2495 ELSE CAST((100. * TotalCPU) / @total_cpu AS MONEY) END AS PercentCPU,
2496 CASE @total_duration WHEN 0 THEN 0
2497 ELSE CAST((100. * TotalDuration) / @total_duration AS MONEY) END AS PercentDuration,
2498 CASE @total_reads WHEN 0 THEN 0
2499 ELSE CAST((100. * TotalReads) / @total_reads AS MONEY) END AS PercentReads,
2500 CASE @total_writes WHEN 0 THEN 0
2501 ELSE CAST((100. * TotalWrites) / @total_writes AS MONEY) END AS PercentWrites,
2502 CASE @total_execution_count WHEN 0 THEN 0
2503 ELSE CAST((100. * ExecutionCount) / @total_execution_count AS MONEY) END AS PercentExecutions,
2504 CASE DATEDIFF(mi, PlanCreationTime, LastExecutionTime)
2505 WHEN 0 THEN 0
2506 ELSE CAST((1.00 * ExecutionCount / DATEDIFF(mi, PlanCreationTime, LastExecutionTime)) AS MONEY)
2507 END AS ExecutionsPerMinute
2508 FROM (
2509 SELECT DatabaseName,
2510 SqlHandle,
2511 QueryHash,
2512 TotalCPU,
2513 TotalDuration,
2514 TotalReads,
2515 TotalWrites,
2516 ExecutionCount,
2517 PlanCreationTime,
2518 LastExecutionTime
2519 FROM ##BlitzCacheProcs
2520 WHERE SPID = @@SPID
2521 GROUP BY DatabaseName,
2522 SqlHandle,
2523 QueryHash,
2524 TotalCPU,
2525 TotalDuration,
2526 TotalReads,
2527 TotalWrites,
2528 ExecutionCount,
2529 PlanCreationTime,
2530 LastExecutionTime
2531 ) AS x
2532) AS y
2533WHERE ##BlitzCacheProcs.SqlHandle = y.SqlHandle
2534 AND ##BlitzCacheProcs.QueryHash = y.QueryHash
2535 AND ##BlitzCacheProcs.DatabaseName = y.DatabaseName
2536 AND ##BlitzCacheProcs.PlanHandle IS NULL
2537OPTION (RECOMPILE) ;
2538
2539
2540
2541/* Testing using XML nodes to speed up processing */
2542RAISERROR(N'Begin XML nodes processing', 0, 1) WITH NOWAIT;
2543WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2544SELECT QueryHash ,
2545 SqlHandle ,
2546 PlanHandle,
2547 q.n.query('.') AS statement,
2548 0 AS is_cursor
2549INTO #statements
2550FROM ##BlitzCacheProcs p
2551 CROSS APPLY p.QueryPlan.nodes('//p:StmtSimple') AS q(n)
2552WHERE p.SPID = @@SPID
2553OPTION (RECOMPILE) ;
2554
2555WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2556INSERT #statements
2557SELECT QueryHash ,
2558 SqlHandle ,
2559 PlanHandle,
2560 q.n.query('.') AS statement,
2561 1 AS is_cursor
2562FROM ##BlitzCacheProcs p
2563 CROSS APPLY p.QueryPlan.nodes('//p:StmtCursor') AS q(n)
2564WHERE p.SPID = @@SPID
2565OPTION (RECOMPILE) ;
2566
2567WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2568SELECT QueryHash ,
2569 SqlHandle ,
2570 q.n.query('.') AS query_plan
2571INTO #query_plan
2572FROM #statements p
2573 CROSS APPLY p.statement.nodes('//p:QueryPlan') AS q(n)
2574OPTION (RECOMPILE) ;
2575
2576WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2577SELECT QueryHash ,
2578 SqlHandle ,
2579 q.n.query('.') AS relop
2580INTO #relop
2581FROM #query_plan p
2582 CROSS APPLY p.query_plan.nodes('//p:RelOp') AS q(n)
2583OPTION (RECOMPILE) ;
2584
2585-- high level plan stuff
2586RAISERROR(N'Gathering high level plan information', 0, 1) WITH NOWAIT;
2587UPDATE ##BlitzCacheProcs
2588SET NumberOfDistinctPlans = distinct_plan_count,
2589 NumberOfPlans = number_of_plans ,
2590 plan_multiple_plans = CASE WHEN distinct_plan_count < number_of_plans THEN 1 END
2591FROM (
2592 SELECT COUNT(DISTINCT QueryHash) AS distinct_plan_count,
2593 COUNT(QueryHash) AS number_of_plans,
2594 QueryHash
2595 FROM ##BlitzCacheProcs
2596 WHERE SPID = @@SPID
2597 GROUP BY QueryHash
2598) AS x
2599WHERE ##BlitzCacheProcs.QueryHash = x.QueryHash
2600OPTION (RECOMPILE) ;
2601
2602-- query level checks
2603RAISERROR(N'Performing query level checks', 0, 1) WITH NOWAIT;
2604WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2605UPDATE ##BlitzCacheProcs
2606SET missing_index_count = query_plan.value('count(//p:QueryPlan/p:MissingIndexes/p:MissingIndexGroup)', 'int') ,
2607 unmatched_index_count = CASE WHEN is_trivial <> 1 THEN query_plan.value('count(//p:QueryPlan/p:UnmatchedIndexes/p:Parameterization/p:Object)', 'int') END ,
2608 SerialDesiredMemory = query_plan.value('sum(//p:QueryPlan/p:MemoryGrantInfo/@SerialDesiredMemory)', 'float') ,
2609 SerialRequiredMemory = query_plan.value('sum(//p:QueryPlan/p:MemoryGrantInfo/@SerialRequiredMemory)', 'float'),
2610 CachedPlanSize = query_plan.value('sum(//p:QueryPlan/@CachedPlanSize)', 'float') ,
2611 CompileTime = query_plan.value('sum(//p:QueryPlan/@CompileTime)', 'float') ,
2612 CompileCPU = query_plan.value('sum(//p:QueryPlan/@CompileCPU)', 'float') ,
2613 CompileMemory = query_plan.value('sum(//p:QueryPlan/@CompileMemory)', 'float'),
2614 MaxCompileMemory = query_plan.value('sum(//p:QueryPlan/p:OptimizerHardwareDependentProperties/@MaxCompileMemory)', 'float')
2615FROM #query_plan qp
2616WHERE qp.QueryHash = ##BlitzCacheProcs.QueryHash
2617AND qp.SqlHandle = ##BlitzCacheProcs.SqlHandle
2618AND SPID = @@SPID
2619OPTION (RECOMPILE);
2620
2621-- statement level checks
2622RAISERROR(N'Performing compile timeout checks', 0, 1) WITH NOWAIT;
2623WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2624UPDATE b
2625SET compile_timeout = 1
2626FROM #statements s
2627JOIN ##BlitzCacheProcs b
2628ON s.QueryHash = b.QueryHash
2629AND SPID = @@SPID
2630WHERE statement.exist('/p:StmtSimple/@StatementOptmEarlyAbortReason[.="TimeOut"]') = 1
2631OPTION (RECOMPILE);
2632
2633RAISERROR(N'Performing compile memory limit exceeded checks', 0, 1) WITH NOWAIT;
2634WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2635UPDATE b
2636SET compile_memory_limit_exceeded = 1
2637FROM #statements s
2638JOIN ##BlitzCacheProcs b
2639ON s.QueryHash = b.QueryHash
2640AND SPID = @@SPID
2641WHERE statement.exist('/p:StmtSimple/@StatementOptmEarlyAbortReason[.="MemoryLimitExceeded"]') = 1
2642OPTION (RECOMPILE);
2643
2644IF @ExpertMode > 0
2645BEGIN
2646RAISERROR(N'Performing unparameterized query checks', 0, 1) WITH NOWAIT;
2647WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p),
2648unparameterized_query AS (
2649 SELECT s.QueryHash,
2650 unparameterized_query = CASE WHEN statement.exist('//p:StmtSimple[@StatementOptmLevel[.="FULL"]]/p:QueryPlan/p:ParameterList') = 1 AND
2651 statement.exist('//p:StmtSimple[@StatementOptmLevel[.="FULL"]]/p:QueryPlan/p:ParameterList/p:ColumnReference') = 0 THEN 1
2652 WHEN statement.exist('//p:StmtSimple[@StatementOptmLevel[.="FULL"]]/p:QueryPlan/p:ParameterList') = 0 AND
2653 statement.exist('//p:StmtSimple[@StatementOptmLevel[.="FULL"]]/*/p:RelOp/descendant::p:ScalarOperator/p:Identifier/p:ColumnReference[contains(@Column, "@")]') = 1 THEN 1
2654 END
2655 FROM #statements AS s
2656 )
2657UPDATE b
2658SET b.unparameterized_query = u.unparameterized_query
2659FROM ##BlitzCacheProcs b
2660JOIN unparameterized_query u
2661ON u.QueryHash = b.QueryHash
2662AND SPID = @@SPID
2663WHERE u.unparameterized_query = 1
2664OPTION (RECOMPILE);
2665END;
2666
2667
2668IF @ExpertMode > 0
2669BEGIN
2670RAISERROR(N'Performing index DML checks', 0, 1) WITH NOWAIT;
2671WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p),
2672index_dml AS (
2673 SELECT s.QueryHash,
2674 index_dml = CASE WHEN statement.exist('//p:StmtSimple/@StatementType[.="CREATE INDEX"]') = 1 THEN 1
2675 WHEN statement.exist('//p:StmtSimple/@StatementType[.="DROP INDEX"]') = 1 THEN 1
2676 END
2677 FROM #statements s
2678 )
2679 UPDATE b
2680 SET b.index_dml = i.index_dml
2681 FROM ##BlitzCacheProcs AS b
2682 JOIN index_dml i
2683 ON i.QueryHash = b.QueryHash
2684 WHERE i.index_dml = 1
2685 AND b.SPID = @@SPID
2686 OPTION (RECOMPILE);
2687END;
2688
2689
2690IF @ExpertMode > 0
2691BEGIN
2692RAISERROR(N'Performing table DML checks', 0, 1) WITH NOWAIT;
2693WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p),
2694table_dml AS (
2695 SELECT s.QueryHash,
2696 table_dml = CASE WHEN statement.exist('//p:StmtSimple/@StatementType[.="CREATE TABLE"]') = 1 THEN 1
2697 WHEN statement.exist('//p:StmtSimple/@StatementType[.="DROP OBJECT"]') = 1 THEN 1
2698 END
2699 FROM #statements AS s
2700 )
2701 UPDATE b
2702 SET b.table_dml = t.table_dml
2703 FROM ##BlitzCacheProcs AS b
2704 JOIN table_dml t
2705 ON t.QueryHash = b.QueryHash
2706 WHERE t.table_dml = 1
2707 AND b.SPID = @@SPID
2708 OPTION (RECOMPILE);
2709END;
2710
2711
2712IF @ExpertMode > 0
2713BEGIN
2714RAISERROR(N'Gathering row estimates', 0, 1) WITH NOWAIT;
2715WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
2716INSERT INTO #est_rows
2717SELECT DISTINCT
2718 CONVERT(BINARY(8), RIGHT('0000000000000000' + SUBSTRING(c.n.value('@QueryHash', 'VARCHAR(18)'), 3, 18), 16), 2) AS QueryHash,
2719 c.n.value('(/p:StmtSimple/@StatementEstRows)[1]', 'FLOAT') AS estimated_rows
2720FROM #statements AS s
2721CROSS APPLY s.statement.nodes('/p:StmtSimple') AS c(n)
2722WHERE c.n.exist('/p:StmtSimple[@StatementEstRows > 0]') = 1;
2723
2724 UPDATE b
2725 SET b.estimated_rows = er.estimated_rows
2726 FROM ##BlitzCacheProcs AS b
2727 JOIN #est_rows er
2728 ON er.QueryHash = b.QueryHash
2729 WHERE b.SPID = @@SPID
2730 AND b.QueryType = 'Statement'
2731 OPTION (RECOMPILE);
2732END;
2733
2734RAISERROR(N'Gathering trivial plans', 0, 1) WITH NOWAIT;
2735WITH XMLNAMESPACES ( 'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
2736UPDATE b
2737SET b.is_trivial = 1
2738FROM ##BlitzCacheProcs AS b
2739JOIN (
2740SELECT s.SqlHandle
2741FROM #statements AS s
2742JOIN ( SELECT r.SqlHandle
2743 FROM #relop AS r
2744 WHERE r.relop.exist('//p:RelOp[contains(@LogicalOp, "Scan")]') = 1 ) AS r
2745 ON r.SqlHandle = s.SqlHandle
2746WHERE s.statement.exist('//p:StmtSimple[@StatementOptmLevel[.="TRIVIAL"]]/p:QueryPlan/p:ParameterList') = 1
2747) AS s
2748ON b.SqlHandle = s.SqlHandle
2749OPTION (RECOMPILE);
2750
2751
2752--Gather costs
2753RAISERROR(N'Gathering statement costs', 0, 1) WITH NOWAIT;
2754WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2755INSERT INTO #plan_cost ( QueryPlanCost, SqlHandle, PlanHandle, QueryHash, QueryPlanHash )
2756SELECT DISTINCT
2757 statement.value('sum(/p:StmtSimple/@StatementSubTreeCost)', 'float') QueryPlanCost,
2758 s.SqlHandle,
2759 s.PlanHandle,
2760 CONVERT(BINARY(8), RIGHT('0000000000000000' + SUBSTRING(q.n.value('@QueryHash', 'VARCHAR(18)'), 3, 18), 16), 2) AS QueryHash,
2761 CONVERT(BINARY(8), RIGHT('0000000000000000' + SUBSTRING(q.n.value('@QueryPlanHash', 'VARCHAR(18)'), 3, 18), 16), 2) AS QueryPlanHash
2762FROM #statements s
2763CROSS APPLY s.statement.nodes('/p:StmtSimple') AS q(n)
2764WHERE statement.value('sum(/p:StmtSimple/@StatementSubTreeCost)', 'float') > 0
2765OPTION (RECOMPILE);
2766
2767RAISERROR(N'Updating statement costs', 0, 1) WITH NOWAIT;
2768WITH pc AS (
2769 SELECT SUM(DISTINCT pc.QueryPlanCost) AS QueryPlanCostSum, pc.QueryHash, pc.QueryPlanHash, pc.SqlHandle, pc.PlanHandle
2770 FROM #plan_cost AS pc
2771 GROUP BY pc.QueryHash, pc.QueryPlanHash, pc.SqlHandle, pc.PlanHandle
2772)
2773 UPDATE b
2774 SET b.QueryPlanCost = ISNULL(pc.QueryPlanCostSum, 0)
2775 FROM pc
2776 JOIN ##BlitzCacheProcs b
2777 ON b.SqlHandle = pc.SqlHandle
2778 AND b.QueryHash = pc.QueryHash
2779 WHERE b.QueryType NOT LIKE '%Procedure%'
2780 OPTION (RECOMPILE);
2781
2782IF EXISTS (
2783SELECT 1
2784FROM ##BlitzCacheProcs AS b
2785WHERE b.QueryType LIKE 'Procedure%'
2786)
2787
2788BEGIN
2789
2790RAISERROR(N'Gathering stored procedure costs', 0, 1) WITH NOWAIT;
2791;WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2792, QueryCost AS (
2793 SELECT
2794 DISTINCT
2795 statement.value('sum(/p:StmtSimple/@StatementSubTreeCost)', 'float') AS SubTreeCost,
2796 s.PlanHandle,
2797 s.SqlHandle
2798 FROM #statements AS s
2799 WHERE PlanHandle IS NOT NULL
2800)
2801, QueryCostUpdate AS (
2802 SELECT
2803 SUM(qc.SubTreeCost) OVER (PARTITION BY SqlHandle, PlanHandle) PlanTotalQuery,
2804 qc.PlanHandle,
2805 qc.SqlHandle
2806 FROM QueryCost qc
2807)
2808INSERT INTO #proc_costs
2809SELECT qcu.PlanTotalQuery, PlanHandle, SqlHandle
2810FROM QueryCostUpdate AS qcu
2811OPTION (RECOMPILE);
2812
2813
2814UPDATE b
2815 SET b.QueryPlanCost = ca.PlanTotalQuery
2816FROM ##BlitzCacheProcs AS b
2817CROSS APPLY (
2818 SELECT TOP 1 PlanTotalQuery
2819 FROM #proc_costs qcu
2820 WHERE qcu.PlanHandle = b.PlanHandle
2821 ORDER BY PlanTotalQuery DESC
2822) ca
2823WHERE b.QueryType LIKE 'Procedure%'
2824AND b.SPID = @@SPID
2825OPTION (RECOMPILE);
2826
2827END;
2828
2829UPDATE b
2830SET b.QueryPlanCost = 0.0
2831FROM ##BlitzCacheProcs b
2832WHERE b.QueryPlanCost IS NULL
2833AND b.SPID = @@SPID
2834OPTION (RECOMPILE);
2835
2836RAISERROR(N'Checking for plan warnings', 0, 1) WITH NOWAIT;
2837WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2838UPDATE ##BlitzCacheProcs
2839SET plan_warnings = 1
2840FROM #query_plan qp
2841WHERE qp.SqlHandle = ##BlitzCacheProcs.SqlHandle
2842AND SPID = @@SPID
2843AND query_plan.exist('/p:QueryPlan/p:Warnings') = 1
2844OPTION (RECOMPILE);
2845
2846RAISERROR(N'Checking for implicit conversion', 0, 1) WITH NOWAIT;
2847WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2848UPDATE ##BlitzCacheProcs
2849SET implicit_conversions = 1
2850FROM #query_plan qp
2851WHERE qp.SqlHandle = ##BlitzCacheProcs.SqlHandle
2852AND SPID = @@SPID
2853AND query_plan.exist('/p:QueryPlan/p:Warnings/p:PlanAffectingConvert/@Expression[contains(., "CONVERT_IMPLICIT")]') = 1
2854OPTION (RECOMPILE);
2855
2856-- operator level checks
2857IF @ExpertMode > 0
2858BEGIN
2859RAISERROR(N'Performing busy loops checks', 0, 1) WITH NOWAIT;
2860WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2861UPDATE p
2862SET busy_loops = CASE WHEN (x.estimated_executions / 100.0) > x.estimated_rows THEN 1 END
2863FROM ##BlitzCacheProcs p
2864 JOIN (
2865 SELECT qs.SqlHandle,
2866 relop.value('sum(/p:RelOp/@EstimateRows)', 'float') AS estimated_rows ,
2867 relop.value('sum(/p:RelOp/@EstimateRewinds)', 'float') + relop.value('sum(/p:RelOp/@EstimateRebinds)', 'float') + 1.0 AS estimated_executions
2868 FROM #relop qs
2869 ) AS x ON p.SqlHandle = x.SqlHandle
2870WHERE SPID = @@SPID
2871OPTION (RECOMPILE);
2872END;
2873
2874
2875RAISERROR(N'Performing TVF join check', 0, 1) WITH NOWAIT;
2876WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2877UPDATE p
2878SET p.tvf_join = CASE WHEN x.tvf_join = 1 THEN 1 END
2879FROM ##BlitzCacheProcs p
2880 JOIN (
2881 SELECT r.SqlHandle,
2882 1 AS tvf_join
2883 FROM #relop AS r
2884 WHERE r.relop.exist('//p:RelOp[(@LogicalOp[.="Table-valued function"])]') = 1
2885 AND r.relop.exist('//p:RelOp[contains(@LogicalOp, "Join")]') = 1
2886 ) AS x ON p.SqlHandle = x.SqlHandle
2887WHERE SPID = @@SPID
2888OPTION (RECOMPILE);
2889
2890IF @ExpertMode > 0
2891BEGIN
2892RAISERROR(N'Checking for operator warnings', 0, 1) WITH NOWAIT;
2893WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2894, x AS (
2895SELECT r.SqlHandle,
2896 c.n.exist('//p:Warnings[(@NoJoinPredicate[.="1"])]') AS warning_no_join_predicate,
2897 c.n.exist('//p:ColumnsWithNoStatistics') AS no_stats_warning ,
2898 c.n.exist('//p:Warnings') AS relop_warnings
2899FROM #relop AS r
2900CROSS APPLY r.relop.nodes('/p:RelOp/p:Warnings') AS c(n)
2901)
2902UPDATE p
2903SET p.warning_no_join_predicate = x.warning_no_join_predicate,
2904 p.no_stats_warning = x.no_stats_warning,
2905 p.relop_warnings = x.relop_warnings
2906FROM ##BlitzCacheProcs AS p
2907JOIN x ON x.SqlHandle = p.SqlHandle
2908AND SPID = @@SPID
2909OPTION (RECOMPILE);
2910END;
2911
2912
2913RAISERROR(N'Checking for table variables', 0, 1) WITH NOWAIT;
2914WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2915, x AS (
2916SELECT r.SqlHandle,
2917 c.n.value('substring(@Table, 2, 1)','VARCHAR(100)') AS first_char
2918FROM #relop r
2919CROSS APPLY r.relop.nodes('//p:Object') AS c(n)
2920)
2921UPDATE p
2922SET is_table_variable = 1
2923FROM ##BlitzCacheProcs AS p
2924JOIN x ON x.SqlHandle = p.SqlHandle
2925AND SPID = @@SPID
2926WHERE x.first_char = '@'
2927OPTION (RECOMPILE);
2928
2929IF @ExpertMode > 0
2930BEGIN
2931RAISERROR(N'Checking for functions', 0, 1) WITH NOWAIT;
2932WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2933, x AS (
2934SELECT qs.SqlHandle,
2935 n.fn.value('count(distinct-values(//p:UserDefinedFunction[not(@IsClrFunction)]))', 'INT') AS function_count,
2936 n.fn.value('count(distinct-values(//p:UserDefinedFunction[@IsClrFunction = "1"]))', 'INT') AS clr_function_count
2937FROM #relop qs
2938CROSS APPLY relop.nodes('/p:RelOp/p:ComputeScalar/p:DefinedValues/p:DefinedValue/p:ScalarOperator') n(fn)
2939)
2940UPDATE p
2941SET p.function_count = x.function_count,
2942 p.clr_function_count = x.clr_function_count
2943FROM ##BlitzCacheProcs AS p
2944JOIN x ON x.SqlHandle = p.SqlHandle
2945AND SPID = @@SPID
2946OPTION (RECOMPILE);
2947END;
2948
2949
2950RAISERROR(N'Checking for expensive key lookups', 0, 1) WITH NOWAIT;
2951WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2952UPDATE ##BlitzCacheProcs
2953SET key_lookup_cost = x.key_lookup_cost
2954FROM (
2955SELECT
2956 qs.SqlHandle,
2957 MAX(relop.value('sum(/p:RelOp/@EstimatedTotalSubtreeCost)', 'float')) AS key_lookup_cost
2958FROM #relop qs
2959WHERE [relop].exist('/p:RelOp/p:IndexScan[(@Lookup[.="1"])]') = 1
2960GROUP BY qs.SqlHandle
2961) AS x
2962WHERE ##BlitzCacheProcs.SqlHandle = x.SqlHandle
2963AND SPID = @@SPID
2964OPTION (RECOMPILE);
2965
2966
2967RAISERROR(N'Checking for expensive remote queries', 0, 1) WITH NOWAIT;
2968WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2969UPDATE ##BlitzCacheProcs
2970SET remote_query_cost = x.remote_query_cost
2971FROM (
2972SELECT
2973 qs.SqlHandle,
2974 MAX(relop.value('sum(/p:RelOp/@EstimatedTotalSubtreeCost)', 'float')) AS remote_query_cost
2975FROM #relop qs
2976WHERE [relop].exist('/p:RelOp[(@PhysicalOp[contains(., "Remote")])]') = 1
2977GROUP BY qs.SqlHandle
2978) AS x
2979WHERE ##BlitzCacheProcs.SqlHandle = x.SqlHandle
2980AND SPID = @@SPID
2981OPTION (RECOMPILE);
2982
2983RAISERROR(N'Checking for expensive sorts', 0, 1) WITH NOWAIT;
2984WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
2985UPDATE ##BlitzCacheProcs
2986SET sort_cost = y.max_sort_cost
2987FROM (
2988 SELECT x.SqlHandle, MAX((x.sort_io + x.sort_cpu)) AS max_sort_cost
2989 FROM (
2990 SELECT
2991 qs.SqlHandle,
2992 relop.value('sum(/p:RelOp/@EstimateIO)', 'float') AS sort_io,
2993 relop.value('sum(/p:RelOp/@EstimateCPU)', 'float') AS sort_cpu
2994 FROM #relop qs
2995 WHERE [relop].exist('/p:RelOp[(@PhysicalOp[.="Sort"])]') = 1
2996 ) AS x
2997 GROUP BY x.SqlHandle
2998 ) AS y
2999WHERE ##BlitzCacheProcs.SqlHandle = y.SqlHandle
3000AND SPID = @@SPID
3001OPTION (RECOMPILE);
3002
3003IF NOT EXISTS(SELECT 1/0 FROM #statements AS s WHERE s.is_cursor = 1)
3004BEGIN
3005
3006RAISERROR(N'No cursor plans found, skipping', 0, 1) WITH NOWAIT;
3007
3008END
3009
3010IF EXISTS(SELECT 1/0 FROM #statements AS s WHERE s.is_cursor = 1)
3011BEGIN
3012
3013RAISERROR(N'Cursor plans found, investigating', 0, 1) WITH NOWAIT;
3014
3015RAISERROR(N'Checking for Optimistic cursors', 0, 1) WITH NOWAIT;
3016WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3017UPDATE b
3018SET b.is_optimistic_cursor = 1
3019FROM ##BlitzCacheProcs b
3020JOIN #statements AS qs
3021ON b.SqlHandle = qs.SqlHandle
3022CROSS APPLY qs.statement.nodes('/p:StmtCursor') AS n1(fn)
3023WHERE SPID = @@SPID
3024AND n1.fn.exist('//p:CursorPlan/@CursorConcurrency[.="Optimistic"]') = 1
3025AND qs.is_cursor = 1
3026OPTION (RECOMPILE);
3027
3028
3029RAISERROR(N'Checking if cursor is Forward Only', 0, 1) WITH NOWAIT;
3030WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3031UPDATE b
3032SET b.is_forward_only_cursor = 1
3033FROM ##BlitzCacheProcs b
3034JOIN #statements AS qs
3035ON b.SqlHandle = qs.SqlHandle
3036CROSS APPLY qs.statement.nodes('/p:StmtCursor') AS n1(fn)
3037WHERE SPID = @@SPID
3038AND n1.fn.exist('//p:CursorPlan/@ForwardOnly[.="true"]') = 1
3039AND qs.is_cursor = 1
3040OPTION (RECOMPILE);
3041
3042RAISERROR(N'Checking if cursor is Fast Forward', 0, 1) WITH NOWAIT;
3043WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3044UPDATE b
3045SET b.is_fast_forward_cursor = 1
3046FROM ##BlitzCacheProcs b
3047JOIN #statements AS qs
3048ON b.SqlHandle = qs.SqlHandle
3049CROSS APPLY qs.statement.nodes('/p:StmtCursor') AS n1(fn)
3050WHERE SPID = @@SPID
3051AND n1.fn.exist('//p:CursorPlan/@CursorActualType[.="FastForward"]') = 1
3052AND qs.is_cursor = 1
3053OPTION (RECOMPILE);
3054
3055
3056RAISERROR(N'Checking for Dynamic cursors', 0, 1) WITH NOWAIT;
3057WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3058UPDATE b
3059SET b.is_cursor_dynamic = 1
3060FROM ##BlitzCacheProcs b
3061JOIN #statements AS qs
3062ON b.SqlHandle = qs.SqlHandle
3063CROSS APPLY qs.statement.nodes('/p:StmtCursor') AS n1(fn)
3064WHERE SPID = @@SPID
3065AND n1.fn.exist('//p:CursorPlan/@CursorActualType[.="Dynamic"]') = 1
3066AND qs.is_cursor = 1
3067OPTION (RECOMPILE);
3068
3069END
3070
3071IF @ExpertMode > 0
3072BEGIN
3073RAISERROR(N'Checking for bad scans and plan forcing', 0, 1) WITH NOWAIT;
3074;WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3075UPDATE b
3076SET
3077b.is_table_scan = x.is_table_scan,
3078b.backwards_scan = x.backwards_scan,
3079b.forced_index = x.forced_index,
3080b.forced_seek = x.forced_seek,
3081b.forced_scan = x.forced_scan
3082FROM ##BlitzCacheProcs b
3083JOIN (
3084SELECT
3085 qs.SqlHandle,
3086 0 AS is_table_scan,
3087 q.n.exist('@ScanDirection[.="BACKWARD"]') AS backwards_scan,
3088 q.n.value('@ForcedIndex', 'bit') AS forced_index,
3089 q.n.value('@ForceSeek', 'bit') AS forced_seek,
3090 q.n.value('@ForceScan', 'bit') AS forced_scan
3091FROM #relop qs
3092CROSS APPLY qs.relop.nodes('//p:IndexScan') AS q(n)
3093UNION ALL
3094SELECT
3095 qs.SqlHandle,
3096 1 AS is_table_scan,
3097 q.n.exist('@ScanDirection[.="BACKWARD"]') AS backwards_scan,
3098 q.n.value('@ForcedIndex', 'bit') AS forced_index,
3099 q.n.value('@ForceSeek', 'bit') AS forced_seek,
3100 q.n.value('@ForceScan', 'bit') AS forced_scan
3101FROM #relop qs
3102CROSS APPLY qs.relop.nodes('//p:TableScan') AS q(n)
3103) AS x ON b.SqlHandle = x.SqlHandle
3104WHERE SPID = @@SPID
3105OPTION (RECOMPILE);
3106END;
3107
3108
3109IF @ExpertMode > 0
3110BEGIN
3111RAISERROR(N'Checking for computed columns that reference scalar UDFs', 0, 1) WITH NOWAIT;
3112WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3113UPDATE ##BlitzCacheProcs
3114SET is_computed_scalar = x.computed_column_function
3115FROM (
3116SELECT qs.SqlHandle,
3117 n.fn.value('count(distinct-values(//p:UserDefinedFunction[not(@IsClrFunction)]))', 'INT') AS computed_column_function
3118FROM #relop qs
3119CROSS APPLY relop.nodes('/p:RelOp/p:ComputeScalar/p:DefinedValues/p:DefinedValue/p:ScalarOperator') n(fn)
3120WHERE n.fn.exist('/p:RelOp/p:ComputeScalar/p:DefinedValues/p:DefinedValue/p:ColumnReference[(@ComputedColumn[.="1"])]') = 1
3121) AS x
3122WHERE ##BlitzCacheProcs.SqlHandle = x.SqlHandle
3123AND SPID = @@SPID
3124OPTION (RECOMPILE);
3125END;
3126
3127
3128RAISERROR(N'Checking for filters that reference scalar UDFs', 0, 1) WITH NOWAIT;
3129WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3130UPDATE ##BlitzCacheProcs
3131SET is_computed_filter = x.filter_function
3132FROM (
3133SELECT
3134r.SqlHandle,
3135c.n.value('count(distinct-values(//p:UserDefinedFunction[not(@IsClrFunction)]))', 'INT') AS filter_function
3136FROM #relop AS r
3137CROSS APPLY r.relop.nodes('/p:RelOp/p:Filter/p:Predicate/p:ScalarOperator/p:Compare/p:ScalarOperator/p:UserDefinedFunction') c(n)
3138) x
3139WHERE ##BlitzCacheProcs.SqlHandle = x.SqlHandle
3140AND SPID = @@SPID
3141OPTION (RECOMPILE);
3142
3143IF @ExpertMode > 0
3144BEGIN
3145RAISERROR(N'Checking modification queries that hit lots of indexes', 0, 1) WITH NOWAIT;
3146WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p),
3147IndexOps AS
3148(
3149 SELECT
3150 r.QueryHash,
3151 c.n.value('@PhysicalOp', 'VARCHAR(100)') AS op_name,
3152 c.n.exist('@PhysicalOp[.="Index Insert"]') AS ii,
3153 c.n.exist('@PhysicalOp[.="Index Update"]') AS iu,
3154 c.n.exist('@PhysicalOp[.="Index Delete"]') AS id,
3155 c.n.exist('@PhysicalOp[.="Clustered Index Insert"]') AS cii,
3156 c.n.exist('@PhysicalOp[.="Clustered Index Update"]') AS ciu,
3157 c.n.exist('@PhysicalOp[.="Clustered Index Delete"]') AS cid,
3158 c.n.exist('@PhysicalOp[.="Table Insert"]') AS ti,
3159 c.n.exist('@PhysicalOp[.="Table Update"]') AS tu,
3160 c.n.exist('@PhysicalOp[.="Table Delete"]') AS td
3161 FROM #relop AS r
3162 CROSS APPLY r.relop.nodes('/p:RelOp') c(n)
3163 OUTER APPLY r.relop.nodes('/p:RelOp/p:ScalarInsert/p:Object') q(n)
3164 OUTER APPLY r.relop.nodes('/p:RelOp/p:Update/p:Object') o2(n)
3165 OUTER APPLY r.relop.nodes('/p:RelOp/p:SimpleUpdate/p:Object') o3(n)
3166), iops AS
3167(
3168 SELECT ios.QueryHash,
3169 SUM(CONVERT(TINYINT, ios.ii)) AS index_insert_count,
3170 SUM(CONVERT(TINYINT, ios.iu)) AS index_update_count,
3171 SUM(CONVERT(TINYINT, ios.id)) AS index_delete_count,
3172 SUM(CONVERT(TINYINT, ios.cii)) AS cx_insert_count,
3173 SUM(CONVERT(TINYINT, ios.ciu)) AS cx_update_count,
3174 SUM(CONVERT(TINYINT, ios.cid)) AS cx_delete_count,
3175 SUM(CONVERT(TINYINT, ios.ti)) AS table_insert_count,
3176 SUM(CONVERT(TINYINT, ios.tu)) AS table_update_count,
3177 SUM(CONVERT(TINYINT, ios.td)) AS table_delete_count
3178 FROM IndexOps AS ios
3179 WHERE ios.op_name IN ('Index Insert', 'Index Delete', 'Index Update',
3180 'Clustered Index Insert', 'Clustered Index Delete', 'Clustered Index Update',
3181 'Table Insert', 'Table Delete', 'Table Update')
3182 GROUP BY ios.QueryHash)
3183UPDATE b
3184SET b.index_insert_count = iops.index_insert_count,
3185 b.index_update_count = iops.index_update_count,
3186 b.index_delete_count = iops.index_delete_count,
3187 b.cx_insert_count = iops.cx_insert_count,
3188 b.cx_update_count = iops.cx_update_count,
3189 b.cx_delete_count = iops.cx_delete_count,
3190 b.table_insert_count = iops.table_insert_count,
3191 b.table_update_count = iops.table_update_count,
3192 b.table_delete_count = iops.table_delete_count
3193FROM ##BlitzCacheProcs AS b
3194JOIN iops ON iops.QueryHash = b.QueryHash
3195WHERE SPID = @@SPID
3196OPTION (RECOMPILE);
3197END;
3198
3199
3200IF @ExpertMode > 0
3201BEGIN
3202RAISERROR(N'Checking for Spatial index use', 0, 1) WITH NOWAIT;
3203WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3204UPDATE ##BlitzCacheProcs
3205SET is_spatial = x.is_spatial
3206FROM (
3207SELECT qs.SqlHandle,
3208 1 AS is_spatial
3209FROM #relop qs
3210CROSS APPLY relop.nodes('/p:RelOp//p:Object') n(fn)
3211WHERE n.fn.exist('(@IndexKind[.="Spatial"])') = 1
3212) AS x
3213WHERE ##BlitzCacheProcs.SqlHandle = x.SqlHandle
3214AND SPID = @@SPID
3215OPTION (RECOMPILE);
3216END;
3217
3218
3219RAISERROR('Checking for wonky Index Spools', 0, 1) WITH NOWAIT;
3220WITH XMLNAMESPACES (
3221 'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3222, selects
3223AS ( SELECT s.QueryHash
3224 FROM #statements AS s
3225 WHERE s.statement.exist('/p:StmtSimple/@StatementType[.="SELECT"]') = 1 )
3226, spools
3227AS ( SELECT DISTINCT r.QueryHash,
3228 c.n.value('@EstimateRows', 'FLOAT') AS estimated_rows,
3229 c.n.value('@EstimateIO', 'FLOAT') AS estimated_io,
3230 c.n.value('@EstimateCPU', 'FLOAT') AS estimated_cpu,
3231 c.n.value('@EstimateRebinds', 'FLOAT') AS estimated_rebinds
3232FROM #relop AS r
3233JOIN selects AS s
3234ON s.QueryHash = r.QueryHash
3235CROSS APPLY r.relop.nodes('/p:RelOp') AS c(n)
3236WHERE r.relop.exist('/p:RelOp[@PhysicalOp="Index Spool" and @LogicalOp="Eager Spool"]') = 1
3237)
3238UPDATE b
3239 SET b.index_spool_rows = sp.estimated_rows,
3240 b.index_spool_cost = ((sp.estimated_io * sp.estimated_cpu) * CASE WHEN sp.estimated_rebinds < 1 THEN 1 ELSE sp.estimated_rebinds END)
3241FROM ##BlitzCacheProcs b
3242JOIN spools sp
3243ON sp.QueryHash = b.QueryHash
3244OPTION (RECOMPILE);
3245
3246
3247/* 2012+ only */
3248IF @v >= 11
3249BEGIN
3250
3251 RAISERROR(N'Checking for forced serialization', 0, 1) WITH NOWAIT;
3252 WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3253 UPDATE ##BlitzCacheProcs
3254 SET is_forced_serial = 1
3255 FROM #query_plan qp
3256 WHERE qp.SqlHandle = ##BlitzCacheProcs.SqlHandle
3257 AND SPID = @@SPID
3258 AND query_plan.exist('/p:QueryPlan/@NonParallelPlanReason') = 1
3259 AND (##BlitzCacheProcs.is_parallel = 0 OR ##BlitzCacheProcs.is_parallel IS NULL)
3260 OPTION (RECOMPILE);
3261
3262 IF @ExpertMode > 0
3263 BEGIN
3264 RAISERROR(N'Checking for ColumnStore queries operating in Row Mode instead of Batch Mode', 0, 1) WITH NOWAIT;
3265 WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3266 UPDATE ##BlitzCacheProcs
3267 SET columnstore_row_mode = x.is_row_mode
3268 FROM (
3269 SELECT
3270 qs.SqlHandle,
3271 relop.exist('/p:RelOp[(@EstimatedExecutionMode[.="Row"])]') AS is_row_mode
3272 FROM #relop qs
3273 WHERE [relop].exist('/p:RelOp/p:IndexScan[(@Storage[.="ColumnStore"])]') = 1
3274 ) AS x
3275 WHERE ##BlitzCacheProcs.SqlHandle = x.SqlHandle
3276 AND SPID = @@SPID
3277 OPTION (RECOMPILE);
3278 END;
3279
3280END;
3281
3282/* 2014+ only */
3283IF @v >= 12
3284BEGIN
3285 RAISERROR('Checking for downlevel cardinality estimators being used on SQL Server 2014.', 0, 1) WITH NOWAIT;
3286
3287 WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3288 UPDATE p
3289 SET downlevel_estimator = CASE WHEN statement.value('min(//p:StmtSimple/@CardinalityEstimationModelVersion)', 'int') < (@v * 10) THEN 1 END
3290 FROM ##BlitzCacheProcs p
3291 JOIN #statements s ON p.QueryHash = s.QueryHash
3292 WHERE SPID = @@SPID
3293 OPTION (RECOMPILE);
3294END ;
3295
3296/* 2016+ only */
3297IF @v >= 13 AND @ExpertMode > 0
3298BEGIN
3299 RAISERROR('Checking for row level security in 2016 only', 0, 1) WITH NOWAIT;
3300
3301 WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3302 UPDATE p
3303 SET p.is_row_level = 1
3304 FROM ##BlitzCacheProcs p
3305 JOIN #statements s ON p.QueryHash = s.QueryHash
3306 WHERE SPID = @@SPID
3307 AND statement.exist('/p:StmtSimple/@SecurityPolicyApplied[.="true"]') = 1
3308 OPTION (RECOMPILE);
3309END ;
3310
3311/* 2017+ only */
3312IF @v >= 14 OR (@v = 13 AND @build >= 5026)
3313BEGIN
3314
3315IF @ExpertMode > 0
3316BEGIN
3317RAISERROR('Gathering stats information', 0, 1) WITH NOWAIT;
3318WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3319INSERT INTO #stats_agg
3320SELECT qp.SqlHandle,
3321 x.c.value('@LastUpdate', 'DATETIME2(7)') AS LastUpdate,
3322 x.c.value('@ModificationCount', 'BIGINT') AS ModificationCount,
3323 x.c.value('@SamplingPercent', 'FLOAT') AS SamplingPercent,
3324 x.c.value('@Statistics', 'NVARCHAR(258)') AS [Statistics],
3325 x.c.value('@Table', 'NVARCHAR(258)') AS [Table],
3326 x.c.value('@Schema', 'NVARCHAR(258)') AS [Schema],
3327 x.c.value('@Database', 'NVARCHAR(258)') AS [Database]
3328FROM #query_plan AS qp
3329CROSS APPLY qp.query_plan.nodes('//p:OptimizerStatsUsage/p:StatisticsInfo') x (c)
3330OPTION (RECOMPILE);
3331
3332
3333RAISERROR('Checking for stale stats', 0, 1) WITH NOWAIT;
3334WITH stale_stats AS (
3335 SELECT sa.SqlHandle
3336 FROM #stats_agg AS sa
3337 GROUP BY sa.SqlHandle
3338 HAVING MAX(sa.LastUpdate) <= DATEADD(DAY, -7, SYSDATETIME())
3339 AND AVG(sa.ModificationCount) >= 100000
3340)
3341UPDATE b
3342SET stale_stats = 1
3343FROM ##BlitzCacheProcs b
3344JOIN stale_stats os
3345ON b.SqlHandle = os.SqlHandle
3346AND b.SPID = @@SPID
3347OPTION (RECOMPILE);
3348END;
3349
3350IF @v >= 14 AND @ExpertMode > 0
3351BEGIN
3352RAISERROR('Checking for adaptive joins', 0, 1) WITH NOWAIT;
3353WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p),
3354aj AS (
3355 SELECT
3356 SqlHandle
3357 FROM #relop AS r
3358 CROSS APPLY r.relop.nodes('//p:RelOp') x(c)
3359 WHERE x.c.exist('@IsAdaptive[.=1]') = 1
3360)
3361UPDATE b
3362SET b.is_adaptive = 1
3363FROM ##BlitzCacheProcs b
3364JOIN aj
3365ON b.SqlHandle = aj.SqlHandle
3366AND b.SPID = @@SPID
3367OPTION (RECOMPILE);
3368END;
3369
3370IF ((@v >= 14
3371 OR (@v = 13 AND @build >= 5026)
3372 OR (@v = 12 AND @build >= 6024))
3373 AND @ExpertMode > 0)
3374
3375BEGIN;
3376WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p),
3377row_goals AS(
3378SELECT qs.QueryHash
3379FROM #relop qs
3380WHERE relop.value('sum(/p:RelOp/@EstimateRowsWithoutRowGoal)', 'float') > 0
3381)
3382UPDATE b
3383SET b.is_row_goal = 1
3384FROM ##BlitzCacheProcs b
3385JOIN row_goals
3386ON b.QueryHash = row_goals.QueryHash
3387AND b.SPID = @@SPID
3388OPTION (RECOMPILE);
3389END ;
3390
3391END;
3392
3393
3394/* END Testing using XML nodes to speed up processing */
3395RAISERROR(N'Gathering additional plan level information', 0, 1) WITH NOWAIT;
3396WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3397UPDATE ##BlitzCacheProcs
3398SET NumberOfDistinctPlans = distinct_plan_count,
3399 NumberOfPlans = number_of_plans,
3400 plan_multiple_plans = CASE WHEN distinct_plan_count < number_of_plans THEN 1 END
3401FROM (
3402SELECT COUNT(DISTINCT QueryHash) AS distinct_plan_count,
3403 COUNT(QueryHash) AS number_of_plans,
3404 QueryHash
3405FROM ##BlitzCacheProcs
3406WHERE SPID = @@SPID
3407GROUP BY QueryHash
3408) AS x
3409WHERE ##BlitzCacheProcs.QueryHash = x.QueryHash
3410OPTION (RECOMPILE);
3411
3412/* Update to grab stored procedure name for individual statements */
3413RAISERROR(N'Attempting to get stored procedure name for individual statements', 0, 1) WITH NOWAIT;
3414UPDATE p
3415SET QueryType = QueryType + ' (parent ' +
3416 + QUOTENAME(OBJECT_SCHEMA_NAME(s.object_id, s.database_id))
3417 + '.'
3418 + QUOTENAME(OBJECT_NAME(s.object_id, s.database_id)) + ')'
3419FROM ##BlitzCacheProcs p
3420 JOIN sys.dm_exec_procedure_stats s ON p.SqlHandle = s.sql_handle
3421WHERE QueryType = 'Statement'
3422AND SPID = @@SPID
3423OPTION (RECOMPILE);
3424
3425RAISERROR(N'Attempting to get function name for individual statements', 0, 1) WITH NOWAIT;
3426DECLARE @function_update_sql NVARCHAR(MAX) = N''
3427IF EXISTS (SELECT 1/0 FROM sys.all_objects AS o WHERE o.name = 'dm_exec_function_stats')
3428 BEGIN
3429 SET @function_update_sql = @function_update_sql + N'
3430 UPDATE p
3431 SET QueryType = QueryType + '' (parent '' +
3432 + QUOTENAME(OBJECT_SCHEMA_NAME(s.object_id, s.database_id))
3433 + ''.''
3434 + QUOTENAME(OBJECT_NAME(s.object_id, s.database_id)) + '')''
3435 FROM ##BlitzCacheProcs p
3436 JOIN sys.dm_exec_function_stats s ON p.SqlHandle = s.sql_handle
3437 WHERE QueryType = ''Statement''
3438 AND SPID = @@SPID
3439 OPTION (RECOMPILE);
3440 '
3441 EXEC sys.sp_executesql @function_update_sql
3442 END
3443
3444
3445/* Trace Flag Checks 2012 SP3, 2014 SP2 and 2016 SP1 only)*/
3446IF @v >= 11
3447BEGIN
3448
3449RAISERROR(N'Trace flag checks', 0, 1) WITH NOWAIT;
3450;WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3451, tf_pretty AS (
3452SELECT qp.QueryHash,
3453 qp.SqlHandle,
3454 q.n.value('@Value', 'INT') AS trace_flag,
3455 q.n.value('@Scope', 'VARCHAR(10)') AS scope
3456FROM #query_plan qp
3457CROSS APPLY qp.query_plan.nodes('/p:QueryPlan/p:TraceFlags/p:TraceFlag') AS q(n)
3458)
3459INSERT INTO #trace_flags
3460SELECT DISTINCT tf1.SqlHandle , tf1.QueryHash,
3461 STUFF((
3462 SELECT DISTINCT ', ' + CONVERT(VARCHAR(5), tf2.trace_flag)
3463 FROM tf_pretty AS tf2
3464 WHERE tf1.SqlHandle = tf2.SqlHandle
3465 AND tf1.QueryHash = tf2.QueryHash
3466 AND tf2.scope = 'Global'
3467 FOR XML PATH(N'')), 1, 2, N''
3468 ) AS global_trace_flags,
3469 STUFF((
3470 SELECT DISTINCT ', ' + CONVERT(VARCHAR(5), tf2.trace_flag)
3471 FROM tf_pretty AS tf2
3472 WHERE tf1.SqlHandle = tf2.SqlHandle
3473 AND tf1.QueryHash = tf2.QueryHash
3474 AND tf2.scope = 'Session'
3475 FOR XML PATH(N'')), 1, 2, N''
3476 ) AS session_trace_flags
3477FROM tf_pretty AS tf1
3478OPTION (RECOMPILE);
3479
3480UPDATE p
3481SET p.trace_flags_session = tf.session_trace_flags
3482FROM ##BlitzCacheProcs p
3483JOIN #trace_flags tf ON tf.QueryHash = p.QueryHash
3484WHERE SPID = @@SPID
3485OPTION (RECOMPILE);
3486
3487END;
3488
3489
3490RAISERROR(N'Checking for MSTVFs', 0, 1) WITH NOWAIT;
3491WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3492UPDATE b
3493SET b.is_mstvf = 1
3494FROM #relop AS r
3495JOIN ##BlitzCacheProcs AS b
3496ON b.SqlHandle = r.SqlHandle
3497WHERE r.relop.exist('/p:RelOp[(@EstimateRows="100" or @EstimateRows="1") and @LogicalOp="Table-valued function"]') = 1
3498OPTION (RECOMPILE);
3499
3500
3501IF @ExpertMode > 0
3502BEGIN
3503RAISERROR(N'Checking for many to many merge joins', 0, 1) WITH NOWAIT;
3504WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3505UPDATE b
3506SET b.is_mm_join = 1
3507FROM #relop AS r
3508JOIN ##BlitzCacheProcs AS b
3509ON b.SqlHandle = r.SqlHandle
3510WHERE r.relop.exist('/p:RelOp/p:Merge/@ManyToMany[.="1"]') = 1
3511OPTION (RECOMPILE);
3512END ;
3513
3514
3515IF @ExpertMode > 0
3516BEGIN
3517RAISERROR(N'Is Paul White Electric?', 0, 1) WITH NOWAIT;
3518WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p),
3519is_paul_white_electric AS (
3520SELECT 1 AS [is_paul_white_electric],
3521r.SqlHandle
3522FROM #relop AS r
3523CROSS APPLY r.relop.nodes('//p:RelOp') c(n)
3524WHERE c.n.exist('@PhysicalOp[.="Switch"]') = 1
3525)
3526UPDATE b
3527SET b.is_paul_white_electric = ipwe.is_paul_white_electric
3528FROM ##BlitzCacheProcs AS b
3529JOIN is_paul_white_electric ipwe
3530ON ipwe.SqlHandle = b.SqlHandle
3531WHERE b.SPID = @@SPID
3532OPTION (RECOMPILE);
3533END ;
3534
3535
3536RAISERROR(N'Checking for non-sargable predicates', 0, 1) WITH NOWAIT;
3537WITH XMLNAMESPACES ( 'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3538, nsarg
3539 AS ( SELECT r.QueryHash, 1 AS fn, 0 AS jo, 0 AS lk
3540 FROM #relop AS r
3541 CROSS APPLY r.relop.nodes('/p:RelOp/p:IndexScan/p:Predicate/p:ScalarOperator/p:Compare/p:ScalarOperator') AS ca(x)
3542 WHERE ( ca.x.exist('//p:ScalarOperator/p:Intrinsic/@FunctionName') = 1
3543 OR ca.x.exist('//p:ScalarOperator/p:IF') = 1 )
3544 UNION ALL
3545 SELECT r.QueryHash, 0 AS fn, 1 AS jo, 0 AS lk
3546 FROM #relop AS r
3547 CROSS APPLY r.relop.nodes('/p:RelOp//p:ScalarOperator') AS ca(x)
3548 WHERE r.relop.exist('/p:RelOp[contains(@LogicalOp, "Join")]') = 1
3549 AND ca.x.exist('//p:ScalarOperator[contains(@ScalarString, "Expr")]') = 1
3550 UNION ALL
3551 SELECT r.QueryHash, 0 AS fn, 0 AS jo, 1 AS lk
3552 FROM #relop AS r
3553 CROSS APPLY r.relop.nodes('/p:RelOp/p:IndexScan/p:Predicate/p:ScalarOperator') AS ca(x)
3554 CROSS APPLY ca.x.nodes('//p:Const') AS co(x)
3555 WHERE ca.x.exist('//p:ScalarOperator/p:Intrinsic/@FunctionName[.="like"]') = 1
3556 AND ( ( co.x.value('substring(@ConstValue, 1, 1)', 'VARCHAR(100)') <> 'N'
3557 AND co.x.value('substring(@ConstValue, 2, 1)', 'VARCHAR(100)') = '%' )
3558 OR ( co.x.value('substring(@ConstValue, 1, 1)', 'VARCHAR(100)') = 'N'
3559 AND co.x.value('substring(@ConstValue, 3, 1)', 'VARCHAR(100)') = '%' ))),
3560 d_nsarg
3561 AS ( SELECT DISTINCT
3562 nsarg.QueryHash
3563 FROM nsarg
3564 WHERE nsarg.fn = 1
3565 OR nsarg.jo = 1
3566 OR nsarg.lk = 1 )
3567UPDATE b
3568SET b.is_nonsargable = 1
3569FROM d_nsarg AS d
3570JOIN ##BlitzCacheProcs AS b
3571 ON b.QueryHash = d.QueryHash
3572WHERE b.SPID = @@SPID
3573OPTION ( RECOMPILE );
3574
3575/*Begin implicit conversion and parameter info */
3576
3577RAISERROR(N'Getting information about implicit conversions and stored proc parameters', 0, 1) WITH NOWAIT;
3578
3579RAISERROR(N'Getting variable info', 0, 1) WITH NOWAIT;
3580WITH XMLNAMESPACES ( 'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3581INSERT #variable_info ( SPID, QueryHash, SqlHandle, proc_name, variable_name, variable_datatype, compile_time_value )
3582SELECT DISTINCT @@SPID,
3583 qp.QueryHash,
3584 qp.SqlHandle,
3585 b.QueryType AS proc_name,
3586 q.n.value('@Column', 'NVARCHAR(258)') AS variable_name,
3587 q.n.value('@ParameterDataType', 'NVARCHAR(258)') AS variable_datatype,
3588 q.n.value('@ParameterCompiledValue', 'NVARCHAR(258)') AS compile_time_value
3589FROM #query_plan AS qp
3590JOIN ##BlitzCacheProcs AS b
3591ON (b.QueryType = 'adhoc' AND b.QueryHash = qp.QueryHash)
3592OR (b.QueryType <> 'adhoc' AND b.SqlHandle = qp.SqlHandle)
3593CROSS APPLY qp.query_plan.nodes('//p:QueryPlan/p:ParameterList/p:ColumnReference') AS q(n)
3594WHERE b.SPID = @@SPID
3595OPTION (RECOMPILE);
3596
3597
3598RAISERROR(N'Getting conversion info', 0, 1) WITH NOWAIT;
3599WITH XMLNAMESPACES ( 'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3600INSERT #conversion_info ( SPID, QueryHash, SqlHandle, proc_name, expression )
3601SELECT DISTINCT @@SPID,
3602 qp.QueryHash,
3603 qp.SqlHandle,
3604 b.QueryType AS proc_name,
3605 qq.c.value('@Expression', 'NVARCHAR(4000)') AS expression
3606FROM #query_plan AS qp
3607JOIN ##BlitzCacheProcs AS b
3608ON (b.QueryType = 'adhoc' AND b.QueryHash = qp.QueryHash)
3609OR (b.QueryType <> 'adhoc' AND b.SqlHandle = qp.SqlHandle)
3610CROSS APPLY qp.query_plan.nodes('//p:QueryPlan/p:Warnings/p:PlanAffectingConvert') AS qq(c)
3611WHERE qq.c.exist('@ConvertIssue[.="Seek Plan"]') = 1
3612 AND qp.QueryHash IS NOT NULL
3613 AND b.implicit_conversions = 1
3614AND b.SPID = @@SPID
3615OPTION (RECOMPILE);
3616
3617
3618RAISERROR(N'Parsing conversion info', 0, 1) WITH NOWAIT;
3619INSERT #stored_proc_info ( SPID, SqlHandle, QueryHash, proc_name, variable_name, variable_datatype, converted_column_name, column_name, converted_to, compile_time_value )
3620SELECT @@SPID AS SPID,
3621 ci.SqlHandle,
3622 ci.QueryHash,
3623 REPLACE(REPLACE(REPLACE(ci.proc_name, ')', ''), 'Statement (parent ', ''), 'Procedure or Function: ', '') AS proc_name,
3624 CASE WHEN ci.at_charindex > 0
3625 AND ci.bracket_charindex > 0
3626 THEN SUBSTRING(ci.expression, ci.at_charindex, ci.bracket_charindex)
3627 ELSE N'**no_variable**'
3628 END AS variable_name,
3629 N'**no_variable**' AS variable_datatype,
3630 CASE WHEN ci.at_charindex = 0
3631 AND ci.comma_charindex > 0
3632 AND ci.second_comma_charindex > 0
3633 THEN SUBSTRING(ci.expression, ci.comma_charindex, ci.second_comma_charindex)
3634 ELSE N'**no_column**'
3635 END AS converted_column_name,
3636 CASE WHEN ci.at_charindex = 0
3637 AND ci.equal_charindex > 0
3638 AND ci.convert_implicit_charindex = 0
3639 THEN SUBSTRING(ci.expression, ci.equal_charindex, 4000)
3640 WHEN ci.at_charindex = 0
3641 AND (ci.equal_charindex -1) > 0
3642 AND ci.convert_implicit_charindex > 0
3643 THEN SUBSTRING(ci.expression, 0, ci.equal_charindex -1)
3644 WHEN ci.at_charindex > 0
3645 AND ci.comma_charindex > 0
3646 AND ci.second_comma_charindex > 0
3647 THEN SUBSTRING(ci.expression, ci.comma_charindex, ci.second_comma_charindex)
3648 ELSE N'**no_column **'
3649 END AS column_name,
3650 CASE WHEN ci.paren_charindex > 0
3651 AND ci.comma_paren_charindex > 0
3652 THEN SUBSTRING(ci.expression, ci.paren_charindex, ci.comma_paren_charindex)
3653 END AS converted_to,
3654 CASE WHEN ci.at_charindex = 0
3655 AND ci.convert_implicit_charindex = 0
3656 AND ci.proc_name = 'Statement'
3657 THEN SUBSTRING(ci.expression, ci.equal_charindex, 4000)
3658 ELSE '**idk_man**'
3659 END AS compile_time_value
3660FROM #conversion_info AS ci
3661OPTION (RECOMPILE);
3662
3663
3664RAISERROR(N'Updating variables for inserted procs', 0, 1) WITH NOWAIT;
3665UPDATE sp
3666SET sp.variable_datatype = vi.variable_datatype,
3667 sp.compile_time_value = vi.compile_time_value
3668FROM #stored_proc_info AS sp
3669JOIN #variable_info AS vi
3670ON (sp.proc_name = 'adhoc' AND sp.QueryHash = vi.QueryHash)
3671OR (sp.proc_name <> 'adhoc' AND sp.SqlHandle = vi.SqlHandle)
3672AND sp.variable_name = vi.variable_name
3673OPTION (RECOMPILE);
3674
3675
3676RAISERROR(N'Inserting variables for other procs', 0, 1) WITH NOWAIT;
3677INSERT #stored_proc_info
3678 ( SPID, SqlHandle, QueryHash, variable_name, variable_datatype, compile_time_value, proc_name )
3679SELECT vi.SPID, vi.SqlHandle, vi.QueryHash, vi.variable_name, vi.variable_datatype, vi.compile_time_value, REPLACE(REPLACE(REPLACE(vi.proc_name, ')', ''), 'Statement (parent ', ''), 'Procedure or Function: ', '') AS proc_name
3680FROM #variable_info AS vi
3681WHERE NOT EXISTS
3682(
3683 SELECT *
3684 FROM #stored_proc_info AS sp
3685 WHERE (sp.proc_name = 'adhoc' AND sp.QueryHash = vi.QueryHash)
3686 OR (sp.proc_name <> 'adhoc' AND sp.SqlHandle = vi.SqlHandle)
3687)
3688OPTION (RECOMPILE);
3689
3690
3691RAISERROR(N'Updating procs', 0, 1) WITH NOWAIT;
3692UPDATE s
3693SET s.variable_datatype = CASE WHEN s.variable_datatype LIKE '%(%)%'
3694 THEN LEFT(s.variable_datatype, CHARINDEX('(', s.variable_datatype) - 1)
3695 ELSE s.variable_datatype
3696 END,
3697 s.converted_to = CASE WHEN s.converted_to LIKE '%(%)%'
3698 THEN LEFT(s.converted_to, CHARINDEX('(', s.converted_to) - 1)
3699 ELSE s.converted_to
3700 END,
3701 s.compile_time_value = CASE WHEN s.compile_time_value LIKE '%(%)%'
3702 THEN SUBSTRING(s.compile_time_value,
3703 CHARINDEX('(', s.compile_time_value) + 1,
3704 CHARINDEX(')', s.compile_time_value) - 1 - CHARINDEX('(', s.compile_time_value)
3705 )
3706 WHEN variable_datatype NOT IN ('bit', 'tinyint', 'smallint', 'int', 'bigint')
3707 AND s.variable_datatype NOT LIKE '%binary%'
3708 AND s.compile_time_value NOT LIKE 'N''%'''
3709 AND s.compile_time_value NOT LIKE '''%'''
3710 AND s.compile_time_value <> s.column_name
3711 AND s.compile_time_value <> '**idk_man**'
3712 THEN QUOTENAME(compile_time_value, '''')
3713 ELSE s.compile_time_value
3714 END
3715FROM #stored_proc_info AS s
3716OPTION (RECOMPILE);
3717
3718
3719RAISERROR(N'Updating SET options', 0, 1) WITH NOWAIT;
3720WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
3721UPDATE s
3722SET set_options = set_options.ansi_set_options
3723FROM #stored_proc_info AS s
3724JOIN (
3725 SELECT x.SqlHandle,
3726 N'SET ANSI_NULLS ' + CASE WHEN [ANSI_NULLS] = 'true' THEN N'ON ' ELSE N'OFF ' END + NCHAR(10) +
3727 N'SET ANSI_PADDING ' + CASE WHEN [ANSI_PADDING] = 'true' THEN N'ON ' ELSE N'OFF ' END + NCHAR(10) +
3728 N'SET ANSI_WARNINGS ' + CASE WHEN [ANSI_WARNINGS] = 'true' THEN N'ON ' ELSE N'OFF ' END + NCHAR(10) +
3729 N'SET ARITHABORT ' + CASE WHEN [ARITHABORT] = 'true' THEN N'ON ' ELSE N' OFF ' END + NCHAR(10) +
3730 N'SET CONCAT_NULL_YIELDS_NULL ' + CASE WHEN [CONCAT_NULL_YIELDS_NULL] = 'true' THEN N'ON ' ELSE N'OFF ' END + NCHAR(10) +
3731 N'SET NUMERIC_ROUNDABORT ' + CASE WHEN [NUMERIC_ROUNDABORT] = 'true' THEN N'ON ' ELSE N'OFF ' END + NCHAR(10) +
3732 N'SET QUOTED_IDENTIFIER ' + CASE WHEN [QUOTED_IDENTIFIER] = 'true' THEN N'ON ' ELSE N'OFF ' + NCHAR(10) END AS [ansi_set_options]
3733 FROM (
3734 SELECT
3735 s.SqlHandle,
3736 so.o.value('@ANSI_NULLS', 'NVARCHAR(20)') AS [ANSI_NULLS],
3737 so.o.value('@ANSI_PADDING', 'NVARCHAR(20)') AS [ANSI_PADDING],
3738 so.o.value('@ANSI_WARNINGS', 'NVARCHAR(20)') AS [ANSI_WARNINGS],
3739 so.o.value('@ARITHABORT', 'NVARCHAR(20)') AS [ARITHABORT],
3740 so.o.value('@CONCAT_NULL_YIELDS_NULL', 'NVARCHAR(20)') AS [CONCAT_NULL_YIELDS_NULL],
3741 so.o.value('@NUMERIC_ROUNDABORT', 'NVARCHAR(20)') AS [NUMERIC_ROUNDABORT],
3742 so.o.value('@QUOTED_IDENTIFIER', 'NVARCHAR(20)') AS [QUOTED_IDENTIFIER]
3743 FROM #statements AS s
3744 CROSS APPLY s.statement.nodes('//p:StatementSetOptions') AS so(o)
3745 ) AS x
3746) AS set_options ON set_options.SqlHandle = s.SqlHandle
3747OPTION(RECOMPILE);
3748
3749
3750RAISERROR(N'Updating conversion XML', 0, 1) WITH NOWAIT;
3751WITH precheck AS (
3752SELECT spi.SPID,
3753 spi.SqlHandle,
3754 spi.proc_name,
3755 (SELECT
3756 CASE WHEN spi.proc_name <> 'Statement'
3757 THEN N'The stored procedure ' + spi.proc_name
3758 ELSE N'This ad hoc statement'
3759 END
3760 + N' had the following implicit conversions: '
3761 + CHAR(10)
3762 + STUFF((
3763 SELECT DISTINCT
3764 @nl
3765 + CASE WHEN spi2.variable_name <> N'**no_variable**'
3766 THEN N'The variable '
3767 WHEN spi2.variable_name = N'**no_variable**' AND (spi2.column_name = spi2.converted_column_name OR spi2.column_name LIKE '%CONVERT_IMPLICIT%')
3768 THEN N'The compiled value '
3769 WHEN spi2.column_name LIKE '%Expr%'
3770 THEN 'The expression '
3771 ELSE N'The column '
3772 END
3773 + CASE WHEN spi2.variable_name <> N'**no_variable**'
3774 THEN spi2.variable_name
3775 WHEN spi2.variable_name = N'**no_variable**' AND (spi2.column_name = spi2.converted_column_name OR spi2.column_name LIKE '%CONVERT_IMPLICIT%')
3776 THEN spi2.compile_time_value
3777 ELSE spi2.column_name
3778 END
3779 + N' has a data type of '
3780 + CASE WHEN spi2.variable_datatype = N'**no_variable**' THEN spi2.converted_to
3781 ELSE spi2.variable_datatype
3782 END
3783 + N' which caused implicit conversion on the column '
3784 + CASE WHEN spi2.column_name LIKE N'%CONVERT_IMPLICIT%'
3785 THEN spi2.converted_column_name
3786 WHEN spi2.column_name = N'**no_column**'
3787 THEN spi2.converted_column_name
3788 WHEN spi2.converted_column_name = N'**no_column**'
3789 THEN spi2.column_name
3790 WHEN spi2.column_name <> spi2.converted_column_name
3791 THEN spi2.converted_column_name
3792 ELSE spi2.column_name
3793 END
3794 + CASE WHEN spi2.variable_name = N'**no_variable**' AND (spi2.column_name = spi2.converted_column_name OR spi2.column_name LIKE '%CONVERT_IMPLICIT%')
3795 THEN N''
3796 WHEN spi2.column_name LIKE '%Expr%'
3797 THEN N''
3798 WHEN spi2.compile_time_value NOT IN ('**declared in proc**', '**idk_man**')
3799 AND spi2.compile_time_value <> spi2.column_name
3800 THEN ' with the value ' + RTRIM(spi2.compile_time_value)
3801 ELSE N''
3802 END
3803 + '.'
3804 FROM #stored_proc_info AS spi2
3805 WHERE spi.SqlHandle = spi2.SqlHandle
3806 FOR XML PATH(N''), TYPE).value(N'.[1]', N'NVARCHAR(MAX)'), 1, 1, N'')
3807 AS [processing-instruction(ClickMe)] FOR XML PATH(''), TYPE )
3808 AS implicit_conversion_info
3809FROM #stored_proc_info AS spi
3810GROUP BY spi.SPID, spi.SqlHandle, spi.proc_name
3811)
3812UPDATE b
3813SET b.implicit_conversion_info = pk.implicit_conversion_info
3814FROM ##BlitzCacheProcs AS b
3815JOIN precheck pk
3816ON pk.SqlHandle = b.SqlHandle
3817AND pk.SPID = b.SPID
3818OPTION (RECOMPILE);
3819
3820
3821RAISERROR(N'Updating cached parameter XML for stored procs', 0, 1) WITH NOWAIT;
3822WITH precheck AS (
3823SELECT spi.SPID,
3824 spi.SqlHandle,
3825 spi.proc_name,
3826 (SELECT
3827 set_options
3828 + @nl
3829 + @nl
3830 + N'EXEC '
3831 + spi.proc_name
3832 + N' '
3833 + STUFF((
3834 SELECT DISTINCT N', '
3835 + CASE WHEN spi2.variable_name <> N'**no_variable**' AND spi2.compile_time_value <> N'**idk_man**'
3836 THEN spi2.variable_name + N' = '
3837 ELSE @nl + N' We could not find any cached parameter values for this stored proc. '
3838 END
3839 + CASE WHEN spi2.variable_name = N'**no_variable**' OR spi2.compile_time_value = N'**idk_man**'
3840 THEN @nl + N' Possible reasons include declared variables inside the procedure, recompile hints, etc. '
3841 WHEN spi2.compile_time_value = N'NULL'
3842 THEN spi2.compile_time_value
3843 ELSE RTRIM(spi2.compile_time_value)
3844 END
3845 FROM #stored_proc_info AS spi2
3846 WHERE spi.SqlHandle = spi2.SqlHandle
3847 AND spi2.proc_name <> N'Statement'
3848 FOR XML PATH(N''), TYPE).value(N'.[1]', N'NVARCHAR(MAX)'), 1, 1, N'')
3849 AS [processing-instruction(ClickMe)] FOR XML PATH(''), TYPE )
3850 AS cached_execution_parameters
3851FROM #stored_proc_info AS spi
3852GROUP BY spi.SPID, spi.SqlHandle, spi.proc_name, spi.set_options
3853)
3854UPDATE b
3855SET b.cached_execution_parameters = pk.cached_execution_parameters
3856FROM ##BlitzCacheProcs AS b
3857JOIN precheck pk
3858ON pk.SqlHandle = b.SqlHandle
3859AND pk.SPID = b.SPID
3860WHERE b.QueryType <> N'Statement'
3861OPTION (RECOMPILE);
3862
3863
3864RAISERROR(N'Updating cached parameter XML for statements', 0, 1) WITH NOWAIT;
3865WITH precheck AS (
3866SELECT spi.SPID,
3867 spi.SqlHandle,
3868 spi.proc_name,
3869 (SELECT
3870 set_options
3871 + @nl
3872 + @nl
3873 + N' See QueryText column for full query text'
3874 + @nl
3875 + @nl
3876 + STUFF((
3877 SELECT DISTINCT N', '
3878 + CASE WHEN spi2.variable_name <> N'**no_variable**' AND spi2.compile_time_value <> N'**idk_man**'
3879 THEN spi2.variable_name + N' = '
3880 ELSE @nl + N' We could not find any cached parameter values for this stored proc. '
3881 END
3882 + CASE WHEN spi2.variable_name = N'**no_variable**' OR spi2.compile_time_value = N'**idk_man**'
3883 THEN @nl + N' Possible reasons include declared variables inside the procedure, recompile hints, etc. '
3884 WHEN spi2.compile_time_value = N'NULL'
3885 THEN spi2.compile_time_value
3886 ELSE RTRIM(spi2.compile_time_value)
3887 END
3888 FROM #stored_proc_info AS spi2
3889 WHERE spi.SqlHandle = spi2.SqlHandle
3890 AND spi2.proc_name = N'Statement'
3891 AND spi2.variable_name NOT LIKE N'%msparam%'
3892 FOR XML PATH(N''), TYPE).value(N'.[1]', N'NVARCHAR(MAX)'), 1, 1, N'')
3893 AS [processing-instruction(ClickMe)] FOR XML PATH(''), TYPE )
3894 AS cached_execution_parameters
3895FROM #stored_proc_info AS spi
3896GROUP BY spi.SPID, spi.SqlHandle, spi.proc_name, spi.set_options
3897)
3898UPDATE b
3899SET b.cached_execution_parameters = pk.cached_execution_parameters
3900FROM ##BlitzCacheProcs AS b
3901JOIN precheck pk
3902ON pk.SqlHandle = b.SqlHandle
3903AND pk.SPID = b.SPID
3904WHERE b.QueryType = N'Statement'
3905OPTION (RECOMPILE);
3906
3907RAISERROR(N'Filling in implicit conversion and cached plan parameter info', 0, 1) WITH NOWAIT;
3908UPDATE b
3909SET b.implicit_conversion_info = CASE WHEN b.implicit_conversion_info IS NULL
3910 OR CONVERT(NVARCHAR(MAX), b.implicit_conversion_info) = N''
3911 THEN '<?NoNeedToClickMe -- N/A --?>'
3912 ELSE b.implicit_conversion_info END,
3913 b.cached_execution_parameters = CASE WHEN b.cached_execution_parameters IS NULL
3914 OR CONVERT(NVARCHAR(MAX), b.cached_execution_parameters) = N''
3915 THEN '<?NoNeedToClickMe -- N/A --?>'
3916 ELSE b.cached_execution_parameters END
3917FROM ##BlitzCacheProcs AS b
3918WHERE b.SPID = @@SPID
3919OPTION (RECOMPILE);
3920
3921/*End implicit conversion and parameter info*/
3922
3923/*Begin Missing Index*/
3924IF EXISTS ( SELECT 1/0
3925 FROM ##BlitzCacheProcs AS bbcp
3926 WHERE bbcp.missing_index_count > 0
3927 OR bbcp.index_spool_cost > 0
3928 OR bbcp.index_spool_rows > 0
3929 AND bbcp.SPID = @@SPID )
3930
3931 BEGIN
3932 RAISERROR(N'Inserting to #missing_index_xml', 0, 1) WITH NOWAIT;
3933 WITH XMLNAMESPACES ( 'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3934 INSERT #missing_index_xml
3935 SELECT qp.QueryHash,
3936 qp.SqlHandle,
3937 c.mg.value('@Impact', 'FLOAT') AS Impact,
3938 c.mg.query('.') AS cmg
3939 FROM #query_plan AS qp
3940 CROSS APPLY qp.query_plan.nodes('//p:MissingIndexes/p:MissingIndexGroup') AS c(mg)
3941 WHERE qp.QueryHash IS NOT NULL
3942 OPTION(RECOMPILE);
3943
3944 RAISERROR(N'Inserting to #missing_index_schema', 0, 1) WITH NOWAIT;
3945 WITH XMLNAMESPACES ( 'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3946 INSERT #missing_index_schema
3947 SELECT mix.QueryHash, mix.SqlHandle, mix.impact,
3948 c.mi.value('@Database', 'NVARCHAR(128)'),
3949 c.mi.value('@Schema', 'NVARCHAR(128)'),
3950 c.mi.value('@Table', 'NVARCHAR(128)'),
3951 c.mi.query('.')
3952 FROM #missing_index_xml AS mix
3953 CROSS APPLY mix.index_xml.nodes('//p:MissingIndex') AS c(mi)
3954 OPTION(RECOMPILE);
3955
3956 RAISERROR(N'Inserting to #missing_index_usage', 0, 1) WITH NOWAIT;
3957 WITH XMLNAMESPACES ( 'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3958 INSERT #missing_index_usage
3959 SELECT ms.QueryHash, ms.SqlHandle, ms.impact, ms.database_name, ms.schema_name, ms.table_name,
3960 c.cg.value('@Usage', 'NVARCHAR(128)'),
3961 c.cg.query('.')
3962 FROM #missing_index_schema ms
3963 CROSS APPLY ms.index_xml.nodes('//p:ColumnGroup') AS c(cg)
3964 OPTION(RECOMPILE);
3965
3966 RAISERROR(N'Inserting to #missing_index_detail', 0, 1) WITH NOWAIT;
3967 WITH XMLNAMESPACES ( 'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
3968 INSERT #missing_index_detail
3969 SELECT miu.QueryHash,
3970 miu.SqlHandle,
3971 miu.impact,
3972 miu.database_name,
3973 miu.schema_name,
3974 miu.table_name,
3975 miu.usage,
3976 c.c.value('@Name', 'NVARCHAR(128)')
3977 FROM #missing_index_usage AS miu
3978 CROSS APPLY miu.index_xml.nodes('//p:Column') AS c(c)
3979 OPTION (RECOMPILE);
3980
3981 RAISERROR(N'Inserting to missing indexes to #missing_index_pretty', 0, 1) WITH NOWAIT;
3982 INSERT #missing_index_pretty
3983 ( QueryHash, SqlHandle, impact, database_name, schema_name, table_name, equality, inequality, include, executions, query_cost, creation_hours, is_spool )
3984 SELECT DISTINCT m.QueryHash, m.SqlHandle, m.impact, m.database_name, m.schema_name, m.table_name
3985 , STUFF(( SELECT DISTINCT N', ' + ISNULL(m2.column_name, '') AS column_name
3986 FROM #missing_index_detail AS m2
3987 WHERE m2.usage = 'EQUALITY'
3988 AND m.QueryHash = m2.QueryHash
3989 AND m.SqlHandle = m2.SqlHandle
3990 AND m.impact = m2.impact
3991 AND m.database_name = m2.database_name
3992 AND m.schema_name = m2.schema_name
3993 AND m.table_name = m2.table_name
3994 FOR XML PATH(N''), TYPE ).value(N'.[1]', N'NVARCHAR(MAX)'), 1, 2, N'') AS equality
3995 , STUFF(( SELECT DISTINCT N', ' + ISNULL(m2.column_name, '') AS column_name
3996 FROM #missing_index_detail AS m2
3997 WHERE m2.usage = 'INEQUALITY'
3998 AND m.QueryHash = m2.QueryHash
3999 AND m.SqlHandle = m2.SqlHandle
4000 AND m.impact = m2.impact
4001 AND m.database_name = m2.database_name
4002 AND m.schema_name = m2.schema_name
4003 AND m.table_name = m2.table_name
4004 FOR XML PATH(N''), TYPE ).value(N'.[1]', N'NVARCHAR(MAX)'), 1, 2, N'') AS inequality
4005 , STUFF(( SELECT DISTINCT N', ' + ISNULL(m2.column_name, '') AS column_name
4006 FROM #missing_index_detail AS m2
4007 WHERE m2.usage = 'INCLUDE'
4008 AND m.QueryHash = m2.QueryHash
4009 AND m.SqlHandle = m2.SqlHandle
4010 AND m.impact = m2.impact
4011 AND m.database_name = m2.database_name
4012 AND m.schema_name = m2.schema_name
4013 AND m.table_name = m2.table_name
4014 FOR XML PATH(N''), TYPE ).value(N'.[1]', N'NVARCHAR(MAX)'), 1, 2, N'') AS [include],
4015 bbcp.ExecutionCount,
4016 bbcp.QueryPlanCost,
4017 bbcp.PlanCreationTimeHours,
4018 0 as is_spool
4019 FROM #missing_index_detail AS m
4020 JOIN ##BlitzCacheProcs AS bbcp
4021 ON m.SqlHandle = bbcp.SqlHandle
4022 AND m.QueryHash = bbcp.QueryHash
4023 OPTION (RECOMPILE);
4024
4025 RAISERROR(N'Inserting to #index_spool_ugly', 0, 1) WITH NOWAIT;
4026 WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
4027 INSERT #index_spool_ugly
4028 (QueryHash, SqlHandle, impact, database_name, schema_name, table_name, equality, inequality, include, executions, query_cost, creation_hours)
4029 SELECT p.QueryHash,
4030 p.SqlHandle,
4031 (c.n.value('@EstimateIO', 'FLOAT') + (c.n.value('@EstimateCPU', 'FLOAT')))
4032 / ( 1 * NULLIF(p.QueryPlanCost, 0)) * 100 AS impact,
4033 o.n.value('@Database', 'NVARCHAR(128)') AS output_database,
4034 o.n.value('@Schema', 'NVARCHAR(128)') AS output_schema,
4035 o.n.value('@Table', 'NVARCHAR(128)') AS output_table,
4036 k.n.value('@Column', 'NVARCHAR(128)') AS range_column,
4037 e.n.value('@Column', 'NVARCHAR(128)') AS expression_column,
4038 o.n.value('@Column', 'NVARCHAR(128)') AS output_column,
4039 p.ExecutionCount,
4040 p.QueryPlanCost,
4041 p.PlanCreationTimeHours
4042 FROM #relop AS r
4043 JOIN ##BlitzCacheProcs p
4044 ON p.QueryHash = r.QueryHash
4045 CROSS APPLY r.relop.nodes('/p:RelOp') AS c(n)
4046 CROSS APPLY r.relop.nodes('/p:RelOp/p:OutputList/p:ColumnReference') AS o(n)
4047 OUTER APPLY r.relop.nodes('/p:RelOp/p:Spool/p:SeekPredicateNew/p:SeekKeys/p:Prefix/p:RangeColumns/p:ColumnReference') AS k(n)
4048 OUTER APPLY r.relop.nodes('/p:RelOp/p:Spool/p:SeekPredicateNew/p:SeekKeys/p:Prefix/p:RangeExpressions/p:ColumnReference') AS e(n)
4049 WHERE r.relop.exist('/p:RelOp[@PhysicalOp="Index Spool" and @LogicalOp="Eager Spool"]') = 1
4050
4051 RAISERROR(N'Inserting to spools to #missing_index_pretty', 0, 1) WITH NOWAIT;
4052 INSERT #missing_index_pretty
4053 (QueryHash, SqlHandle, impact, database_name, schema_name, table_name, equality, inequality, include, executions, query_cost, creation_hours, is_spool)
4054 SELECT DISTINCT
4055 isu.QueryHash,
4056 isu.SqlHandle,
4057 isu.impact,
4058 isu.database_name,
4059 isu.schema_name,
4060 isu.table_name
4061 , STUFF(( SELECT DISTINCT N', ' + ISNULL(isu2.equality, '') AS column_name
4062 FROM #index_spool_ugly AS isu2
4063 WHERE isu2.equality IS NOT NULL
4064 AND isu.QueryHash = isu2.QueryHash
4065 AND isu.SqlHandle = isu2.SqlHandle
4066 AND isu.impact = isu2.impact
4067 AND isu.database_name = isu2.database_name
4068 AND isu.schema_name = isu2.schema_name
4069 AND isu.table_name = isu2.table_name
4070 FOR XML PATH(N''), TYPE ).value(N'.[1]', N'NVARCHAR(MAX)'), 1, 2, N'') AS equality
4071 , STUFF(( SELECT DISTINCT N', ' + ISNULL(isu2.inequality, '') AS column_name
4072 FROM #index_spool_ugly AS isu2
4073 WHERE isu2.inequality IS NOT NULL
4074 AND isu.QueryHash = isu2.QueryHash
4075 AND isu.SqlHandle = isu2.SqlHandle
4076 AND isu.impact = isu2.impact
4077 AND isu.database_name = isu2.database_name
4078 AND isu.schema_name = isu2.schema_name
4079 AND isu.table_name = isu2.table_name
4080 FOR XML PATH(N''), TYPE ).value(N'.[1]', N'NVARCHAR(MAX)'), 1, 2, N'') AS inequality
4081 , STUFF(( SELECT DISTINCT N', ' + ISNULL(isu2.include, '') AS column_name
4082 FROM #index_spool_ugly AS isu2
4083 WHERE isu2.include IS NOT NULL
4084 AND isu.QueryHash = isu2.QueryHash
4085 AND isu.SqlHandle = isu2.SqlHandle
4086 AND isu.impact = isu2.impact
4087 AND isu.database_name = isu2.database_name
4088 AND isu.schema_name = isu2.schema_name
4089 AND isu.table_name = isu2.table_name
4090 FOR XML PATH(N''), TYPE ).value(N'.[1]', N'NVARCHAR(MAX)'), 1, 2, N'') AS include,
4091 isu.executions,
4092 isu.query_cost,
4093 isu.creation_hours,
4094 1 AS is_spool
4095 FROM #index_spool_ugly AS isu
4096
4097
4098 RAISERROR(N'Updating missing index information', 0, 1) WITH NOWAIT;
4099 WITH missing AS (
4100 SELECT DISTINCT
4101 mip.QueryHash,
4102 mip.SqlHandle,
4103 mip.executions,
4104 N'<MissingIndexes><![CDATA['
4105 + CHAR(10) + CHAR(13)
4106 + STUFF(( SELECT CHAR(10) + CHAR(13) + ISNULL(mip2.details, '') AS details
4107 FROM #missing_index_pretty AS mip2
4108 WHERE mip.QueryHash = mip2.QueryHash
4109 AND mip.SqlHandle = mip2.SqlHandle
4110 AND mip.executions = mip2.executions
4111 GROUP BY mip2.details
4112 ORDER BY MAX(mip2.impact) DESC
4113 FOR XML PATH(N''), TYPE ).value(N'.[1]', N'NVARCHAR(MAX)'), 1, 2, N'')
4114 + CHAR(10) + CHAR(13)
4115 + N']]></MissingIndexes>'
4116 AS full_details
4117 FROM #missing_index_pretty AS mip
4118 )
4119 UPDATE bbcp
4120 SET bbcp.missing_indexes = m.full_details
4121 FROM ##BlitzCacheProcs AS bbcp
4122 JOIN missing AS m
4123 ON m.SqlHandle = bbcp.SqlHandle
4124 AND m.QueryHash = bbcp.QueryHash
4125 AND m.executions = bbcp.ExecutionCount
4126 AND SPID = @@SPID
4127 OPTION (RECOMPILE);
4128
4129 END;
4130
4131 RAISERROR(N'Filling in missing index blanks', 0, 1) WITH NOWAIT;
4132 UPDATE b
4133 SET b.missing_indexes =
4134 CASE WHEN b.missing_indexes IS NULL
4135 THEN '<?NoNeedToClickMe -- N/A --?>'
4136 ELSE b.missing_indexes
4137 END
4138 FROM ##BlitzCacheProcs AS b
4139 WHERE b.SPID = @@SPID
4140 OPTION (RECOMPILE);
4141
4142/*End Missing Index*/
4143
4144
4145/* Set configuration values */
4146RAISERROR(N'Setting configuration values', 0, 1) WITH NOWAIT;
4147DECLARE @execution_threshold INT = 1000 ,
4148 @parameter_sniffing_warning_pct TINYINT = 30,
4149 /* This is in average reads */
4150 @parameter_sniffing_io_threshold BIGINT = 100000 ,
4151 @ctp_threshold_pct TINYINT = 10,
4152 @long_running_query_warning_seconds BIGINT = 300 * 1000 ,
4153 @memory_grant_warning_percent INT = 10;
4154
4155IF EXISTS (SELECT 1/0 FROM #configuration WHERE 'frequent execution threshold' = LOWER(parameter_name))
4156BEGIN
4157 SELECT @execution_threshold = CAST(value AS INT)
4158 FROM #configuration
4159 WHERE 'frequent execution threshold' = LOWER(parameter_name) ;
4160
4161 SET @msg = ' Setting "frequent execution threshold" to ' + CAST(@execution_threshold AS VARCHAR(10)) ;
4162
4163 RAISERROR(@msg, 0, 1) WITH NOWAIT;
4164END;
4165
4166IF EXISTS (SELECT 1/0 FROM #configuration WHERE 'parameter sniffing variance percent' = LOWER(parameter_name))
4167BEGIN
4168 SELECT @parameter_sniffing_warning_pct = CAST(value AS TINYINT)
4169 FROM #configuration
4170 WHERE 'parameter sniffing variance percent' = LOWER(parameter_name) ;
4171
4172 SET @msg = ' Setting "parameter sniffing variance percent" to ' + CAST(@parameter_sniffing_warning_pct AS VARCHAR(3)) ;
4173
4174 RAISERROR(@msg, 0, 1) WITH NOWAIT;
4175END;
4176
4177IF EXISTS (SELECT 1/0 FROM #configuration WHERE 'parameter sniffing io threshold' = LOWER(parameter_name))
4178BEGIN
4179 SELECT @parameter_sniffing_io_threshold = CAST(value AS BIGINT)
4180 FROM #configuration
4181 WHERE 'parameter sniffing io threshold' = LOWER(parameter_name) ;
4182
4183 SET @msg = ' Setting "parameter sniffing io threshold" to ' + CAST(@parameter_sniffing_io_threshold AS VARCHAR(10));
4184
4185 RAISERROR(@msg, 0, 1) WITH NOWAIT;
4186END;
4187
4188IF EXISTS (SELECT 1/0 FROM #configuration WHERE 'cost threshold for parallelism warning' = LOWER(parameter_name))
4189BEGIN
4190 SELECT @ctp_threshold_pct = CAST(value AS TINYINT)
4191 FROM #configuration
4192 WHERE 'cost threshold for parallelism warning' = LOWER(parameter_name) ;
4193
4194 SET @msg = ' Setting "cost threshold for parallelism warning" to ' + CAST(@ctp_threshold_pct AS VARCHAR(3));
4195
4196 RAISERROR(@msg, 0, 1) WITH NOWAIT;
4197END;
4198
4199IF EXISTS (SELECT 1/0 FROM #configuration WHERE 'long running query warning (seconds)' = LOWER(parameter_name))
4200BEGIN
4201 SELECT @long_running_query_warning_seconds = CAST(value * 1000 AS BIGINT)
4202 FROM #configuration
4203 WHERE 'long running query warning (seconds)' = LOWER(parameter_name) ;
4204
4205 SET @msg = ' Setting "long running query warning (seconds)" to ' + CAST(@long_running_query_warning_seconds AS VARCHAR(10));
4206
4207 RAISERROR(@msg, 0, 1) WITH NOWAIT;
4208END;
4209
4210IF EXISTS (SELECT 1/0 FROM #configuration WHERE 'unused memory grant' = LOWER(parameter_name))
4211BEGIN
4212 SELECT @memory_grant_warning_percent = CAST(value AS INT)
4213 FROM #configuration
4214 WHERE 'unused memory grant' = LOWER(parameter_name) ;
4215
4216 SET @msg = ' Setting "unused memory grant" to ' + CAST(@memory_grant_warning_percent AS VARCHAR(10));
4217
4218 RAISERROR(@msg, 0, 1) WITH NOWAIT;
4219END;
4220
4221DECLARE @ctp INT ;
4222
4223SELECT @ctp = NULLIF(CAST(value AS INT), 0)
4224FROM sys.configurations
4225WHERE name = 'cost threshold for parallelism'
4226OPTION (RECOMPILE);
4227
4228
4229/* Update to populate checks columns */
4230RAISERROR('Checking for query level SQL Server issues.', 0, 1) WITH NOWAIT;
4231
4232WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
4233UPDATE ##BlitzCacheProcs
4234SET frequent_execution = CASE WHEN ExecutionsPerMinute > @execution_threshold THEN 1 END ,
4235 parameter_sniffing = CASE WHEN ExecutionCount > 3 AND AverageReads > @parameter_sniffing_io_threshold
4236 AND min_worker_time < ((1.0 - (@parameter_sniffing_warning_pct / 100.0)) * AverageCPU) THEN 1
4237 WHEN ExecutionCount > 3 AND AverageReads > @parameter_sniffing_io_threshold
4238 AND max_worker_time > ((1.0 + (@parameter_sniffing_warning_pct / 100.0)) * AverageCPU) THEN 1
4239 WHEN ExecutionCount > 3 AND AverageReads > @parameter_sniffing_io_threshold
4240 AND MinReturnedRows < ((1.0 - (@parameter_sniffing_warning_pct / 100.0)) * AverageReturnedRows) THEN 1
4241 WHEN ExecutionCount > 3 AND AverageReads > @parameter_sniffing_io_threshold
4242 AND MaxReturnedRows > ((1.0 + (@parameter_sniffing_warning_pct / 100.0)) * AverageReturnedRows) THEN 1 END ,
4243 near_parallel = CASE WHEN QueryPlanCost BETWEEN @ctp * (1 - (@ctp_threshold_pct / 100.0)) AND @ctp THEN 1 END,
4244 long_running = CASE WHEN AverageDuration > @long_running_query_warning_seconds THEN 1
4245 WHEN max_worker_time > @long_running_query_warning_seconds THEN 1
4246 WHEN max_elapsed_time > @long_running_query_warning_seconds THEN 1 END,
4247 is_key_lookup_expensive = CASE WHEN QueryPlanCost >= (@ctp / 2) AND key_lookup_cost >= QueryPlanCost * .5 THEN 1 END,
4248 is_sort_expensive = CASE WHEN QueryPlanCost >= (@ctp / 2) AND sort_cost >= QueryPlanCost * .5 THEN 1 END,
4249 is_remote_query_expensive = CASE WHEN remote_query_cost >= QueryPlanCost * .05 THEN 1 END,
4250 is_unused_grant = CASE WHEN PercentMemoryGrantUsed <= @memory_grant_warning_percent AND MinGrantKB > @MinMemoryPerQuery THEN 1 END,
4251 long_running_low_cpu = CASE WHEN AverageDuration > AverageCPU * 4 AND AverageCPU < 500. THEN 1 END,
4252 low_cost_high_cpu = CASE WHEN QueryPlanCost <= 10 AND AverageCPU > 5000. THEN 1 END,
4253 is_spool_expensive = CASE WHEN QueryPlanCost > (@ctp / 5) AND index_spool_cost >= QueryPlanCost * .1 THEN 1 END,
4254 is_spool_more_rows = CASE WHEN index_spool_rows >= (AverageReturnedRows / ISNULL(NULLIF(ExecutionCount, 0), 1)) THEN 1 END,
4255 is_bad_estimate = CASE WHEN AverageReturnedRows > 0 AND (estimated_rows * 1000 < AverageReturnedRows OR estimated_rows > AverageReturnedRows * 1000) THEN 1 END,
4256 is_big_spills = CASE WHEN (AvgSpills / 128.) > 499. THEN 1 END
4257WHERE SPID = @@SPID
4258OPTION (RECOMPILE);
4259
4260
4261
4262RAISERROR('Checking for forced parameterization and cursors.', 0, 1) WITH NOWAIT;
4263
4264/* Set options checks */
4265UPDATE p
4266 SET is_forced_parameterized = CASE WHEN (CAST(pa.value AS INT) & 131072 = 131072) THEN 1 END ,
4267 is_forced_plan = CASE WHEN (CAST(pa.value AS INT) & 4 = 4) THEN 1 END ,
4268 SetOptions = SUBSTRING(
4269 CASE WHEN (CAST(pa.value AS INT) & 1 = 1) THEN ', ANSI_PADDING' ELSE '' END +
4270 CASE WHEN (CAST(pa.value AS INT) & 8 = 8) THEN ', CONCAT_NULL_YIELDS_NULL' ELSE '' END +
4271 CASE WHEN (CAST(pa.value AS INT) & 16 = 16) THEN ', ANSI_WARNINGS' ELSE '' END +
4272 CASE WHEN (CAST(pa.value AS INT) & 32 = 32) THEN ', ANSI_NULLS' ELSE '' END +
4273 CASE WHEN (CAST(pa.value AS INT) & 64 = 64) THEN ', QUOTED_IDENTIFIER' ELSE '' END +
4274 CASE WHEN (CAST(pa.value AS INT) & 4096 = 4096) THEN ', ARITH_ABORT' ELSE '' END +
4275 CASE WHEN (CAST(pa.value AS INT) & 8192 = 8191) THEN ', NUMERIC_ROUNDABORT' ELSE '' END
4276 , 2, 200000)
4277FROM ##BlitzCacheProcs p
4278 CROSS APPLY sys.dm_exec_plan_attributes(p.PlanHandle) pa
4279WHERE pa.attribute = 'set_options'
4280AND SPID = @@SPID
4281OPTION (RECOMPILE);
4282
4283
4284/* Cursor checks */
4285UPDATE p
4286SET is_cursor = CASE WHEN CAST(pa.value AS INT) <> 0 THEN 1 END
4287FROM ##BlitzCacheProcs p
4288 CROSS APPLY sys.dm_exec_plan_attributes(p.PlanHandle) pa
4289WHERE pa.attribute LIKE '%cursor%'
4290AND SPID = @@SPID
4291OPTION (RECOMPILE);
4292
4293UPDATE p
4294SET is_cursor = 1
4295FROM ##BlitzCacheProcs p
4296WHERE QueryHash = 0x0000000000000000
4297OR QueryPlanHash = 0x0000000000000000
4298AND SPID = @@SPID
4299OPTION (RECOMPILE);
4300
4301
4302
4303RAISERROR('Populating Warnings column', 0, 1) WITH NOWAIT;
4304/* Populate warnings */
4305UPDATE ##BlitzCacheProcs
4306SET Warnings = SUBSTRING(
4307 CASE WHEN warning_no_join_predicate = 1 THEN ', No Join Predicate' ELSE '' END +
4308 CASE WHEN compile_timeout = 1 THEN ', Compilation Timeout' ELSE '' END +
4309 CASE WHEN compile_memory_limit_exceeded = 1 THEN ', Compile Memory Limit Exceeded' ELSE '' END +
4310 CASE WHEN busy_loops = 1 THEN ', Busy Loops' ELSE '' END +
4311 CASE WHEN is_forced_plan = 1 THEN ', Forced Plan' ELSE '' END +
4312 CASE WHEN is_forced_parameterized = 1 THEN ', Forced Parameterization' ELSE '' END +
4313 CASE WHEN unparameterized_query = 1 THEN ', Unparameterized Query' ELSE '' END +
4314 CASE WHEN missing_index_count > 0 THEN ', Missing Indexes (' + CAST(missing_index_count AS VARCHAR(3)) + ')' ELSE '' END +
4315 CASE WHEN unmatched_index_count > 0 THEN ', Unmatched Indexes (' + CAST(unmatched_index_count AS VARCHAR(3)) + ')' ELSE '' END +
4316 CASE WHEN is_cursor = 1 THEN ', Cursor'
4317 + CASE WHEN is_optimistic_cursor = 1 THEN '; optimistic' ELSE '' END
4318 + CASE WHEN is_forward_only_cursor = 0 THEN '; not forward only' ELSE '' END
4319 + CASE WHEN is_cursor_dynamic = 1 THEN '; dynamic' ELSE '' END
4320 + CASE WHEN is_fast_forward_cursor = 1 THEN '; fast forward' ELSE '' END
4321 ELSE '' END +
4322 CASE WHEN is_parallel = 1 THEN ', Parallel' ELSE '' END +
4323 CASE WHEN near_parallel = 1 THEN ', Nearly Parallel' ELSE '' END +
4324 CASE WHEN frequent_execution = 1 THEN ', Frequent Execution' ELSE '' END +
4325 CASE WHEN plan_warnings = 1 THEN ', Plan Warnings' ELSE '' END +
4326 CASE WHEN parameter_sniffing = 1 THEN ', Parameter Sniffing' ELSE '' END +
4327 CASE WHEN long_running = 1 THEN ', Long Running Query' ELSE '' END +
4328 CASE WHEN downlevel_estimator = 1 THEN ', Downlevel CE' ELSE '' END +
4329 CASE WHEN implicit_conversions = 1 THEN ', Implicit Conversions' ELSE '' END +
4330 CASE WHEN tvf_join = 1 THEN ', Function Join' ELSE '' END +
4331 CASE WHEN plan_multiple_plans = 1 THEN ', Multiple Plans' ELSE '' END +
4332 CASE WHEN is_trivial = 1 THEN ', Trivial Plans' ELSE '' END +
4333 CASE WHEN is_forced_serial = 1 THEN ', Forced Serialization' ELSE '' END +
4334 CASE WHEN is_key_lookup_expensive = 1 THEN ', Expensive Key Lookup' ELSE '' END +
4335 CASE WHEN is_remote_query_expensive = 1 THEN ', Expensive Remote Query' ELSE '' END +
4336 CASE WHEN trace_flags_session IS NOT NULL THEN ', Session Level Trace Flag(s) Enabled: ' + trace_flags_session ELSE '' END +
4337 CASE WHEN is_unused_grant = 1 THEN ', Unused Memory Grant' ELSE '' END +
4338 CASE WHEN function_count > 0 THEN ', Calls ' + CONVERT(VARCHAR(10), function_count) + ' function(s)' ELSE '' END +
4339 CASE WHEN clr_function_count > 0 THEN ', Calls ' + CONVERT(VARCHAR(10), clr_function_count) + ' CLR function(s)' ELSE '' END +
4340 CASE WHEN PlanCreationTimeHours <= 4 THEN ', Plan created last 4hrs' ELSE '' END +
4341 CASE WHEN is_table_variable = 1 THEN ', Table Variables' ELSE '' END +
4342 CASE WHEN no_stats_warning = 1 THEN ', Columns With No Statistics' ELSE '' END +
4343 CASE WHEN relop_warnings = 1 THEN ', Operator Warnings' ELSE '' END +
4344 CASE WHEN is_table_scan = 1 THEN ', Table Scans' ELSE '' END +
4345 CASE WHEN backwards_scan = 1 THEN ', Backwards Scans' ELSE '' END +
4346 CASE WHEN forced_index = 1 THEN ', Forced Indexes' ELSE '' END +
4347 CASE WHEN forced_seek = 1 THEN ', Forced Seeks' ELSE '' END +
4348 CASE WHEN forced_scan = 1 THEN ', Forced Scans' ELSE '' END +
4349 CASE WHEN columnstore_row_mode = 1 THEN ', ColumnStore Row Mode ' ELSE '' END +
4350 CASE WHEN is_computed_scalar = 1 THEN ', Computed Column UDF ' ELSE '' END +
4351 CASE WHEN is_sort_expensive = 1 THEN ', Expensive Sort' ELSE '' END +
4352 CASE WHEN is_computed_filter = 1 THEN ', Filter UDF' ELSE '' END +
4353 CASE WHEN index_ops >= 5 THEN ', >= 5 Indexes Modified' ELSE '' END +
4354 CASE WHEN is_row_level = 1 THEN ', Row Level Security' ELSE '' END +
4355 CASE WHEN is_spatial = 1 THEN ', Spatial Index' ELSE '' END +
4356 CASE WHEN index_dml = 1 THEN ', Index DML' ELSE '' END +
4357 CASE WHEN table_dml = 1 THEN ', Table DML' ELSE '' END +
4358 CASE WHEN low_cost_high_cpu = 1 THEN ', Low Cost High CPU' ELSE '' END +
4359 CASE WHEN long_running_low_cpu = 1 THEN + ', Long Running With Low CPU' ELSE '' END +
4360 CASE WHEN stale_stats = 1 THEN + ', Statistics used have > 100k modifications in the last 7 days' ELSE '' END +
4361 CASE WHEN is_adaptive = 1 THEN + ', Adaptive Joins' ELSE '' END +
4362 CASE WHEN is_spool_expensive = 1 THEN + ', Expensive Index Spool' ELSE '' END +
4363 CASE WHEN is_spool_more_rows = 1 THEN + ', Large Index Row Spool' ELSE '' END +
4364 CASE WHEN is_bad_estimate = 1 THEN + ', Row estimate mismatch' ELSE '' END +
4365 CASE WHEN is_paul_white_electric = 1 THEN ', SWITCH!' ELSE '' END +
4366 CASE WHEN is_row_goal = 1 THEN ', Row Goals' ELSE '' END +
4367 CASE WHEN is_big_spills = 1 THEN ', >500mb spills' ELSE '' END +
4368 CASE WHEN is_mstvf = 1 THEN ', MSTVFs' ELSE '' END +
4369 CASE WHEN is_mm_join = 1 THEN ', Many to Many Merge' ELSE '' END +
4370 CASE WHEN is_nonsargable = 1 THEN ', non-SARGables' ELSE '' END +
4371 CASE WHEN CompileTime > 5000 THEN ', Long Compile Time' ELSE '' END +
4372 CASE WHEN CompileCPU > 5000 THEN ', High Compile CPU' ELSE '' END +
4373 CASE WHEN CompileMemory > 1024 AND ((CompileMemory) / (1 * CASE WHEN MaxCompileMemory = 0 THEN 1 ELSE MaxCompileMemory END) * 100.) >= 10. THEN ', High Compile Memory' ELSE '' END
4374 , 3, 200000)
4375WHERE SPID = @@SPID
4376OPTION (RECOMPILE);
4377
4378
4379RAISERROR('Populating Warnings column for stored procedures', 0, 1) WITH NOWAIT;
4380WITH statement_warnings AS
4381 (
4382SELECT DISTINCT
4383 SqlHandle,
4384 Warnings = SUBSTRING(
4385 CASE WHEN warning_no_join_predicate = 1 THEN ', No Join Predicate' ELSE '' END +
4386 CASE WHEN compile_timeout = 1 THEN ', Compilation Timeout' ELSE '' END +
4387 CASE WHEN compile_memory_limit_exceeded = 1 THEN ', Compile Memory Limit Exceeded' ELSE '' END +
4388 CASE WHEN busy_loops = 1 THEN ', Busy Loops' ELSE '' END +
4389 CASE WHEN is_forced_plan = 1 THEN ', Forced Plan' ELSE '' END +
4390 CASE WHEN is_forced_parameterized = 1 THEN ', Forced Parameterization' ELSE '' END +
4391 --CASE WHEN unparameterized_query = 1 THEN ', Unparameterized Query' ELSE '' END +
4392 CASE WHEN missing_index_count > 0 THEN ', Missing Indexes (' + CONVERT(VARCHAR(10), (SELECT SUM(b2.missing_index_count) FROM ##BlitzCacheProcs AS b2 WHERE b2.SqlHandle = b.SqlHandle AND b2.QueryHash IS NOT NULL) ) + ')' ELSE '' END +
4393 CASE WHEN unmatched_index_count > 0 THEN ', Unmatched Indexes (' + CONVERT(VARCHAR(10), (SELECT SUM(b2.unmatched_index_count) FROM ##BlitzCacheProcs AS b2 WHERE b2.SqlHandle = b.SqlHandle AND b2.QueryHash IS NOT NULL) ) + ')' ELSE '' END +
4394 CASE WHEN is_cursor = 1 THEN ', Cursor'
4395 + CASE WHEN is_optimistic_cursor = 1 THEN '; optimistic' ELSE '' END
4396 + CASE WHEN is_forward_only_cursor = 0 THEN '; not forward only' ELSE '' END
4397 + CASE WHEN is_cursor_dynamic = 1 THEN '; dynamic' ELSE '' END
4398 + CASE WHEN is_fast_forward_cursor = 1 THEN '; fast forward' ELSE '' END
4399 ELSE '' END +
4400 CASE WHEN is_parallel = 1 THEN ', Parallel' ELSE '' END +
4401 CASE WHEN near_parallel = 1 THEN ', Nearly Parallel' ELSE '' END +
4402 CASE WHEN frequent_execution = 1 THEN ', Frequent Execution' ELSE '' END +
4403 CASE WHEN plan_warnings = 1 THEN ', Plan Warnings' ELSE '' END +
4404 CASE WHEN parameter_sniffing = 1 THEN ', Parameter Sniffing' ELSE '' END +
4405 CASE WHEN long_running = 1 THEN ', Long Running Query' ELSE '' END +
4406 CASE WHEN downlevel_estimator = 1 THEN ', Downlevel CE' ELSE '' END +
4407 CASE WHEN implicit_conversions = 1 THEN ', Implicit Conversions' ELSE '' END +
4408 CASE WHEN tvf_join = 1 THEN ', Function Join' ELSE '' END +
4409 CASE WHEN plan_multiple_plans = 1 THEN ', Multiple Plans' ELSE '' END +
4410 CASE WHEN is_trivial = 1 THEN ', Trivial Plans' ELSE '' END +
4411 CASE WHEN is_forced_serial = 1 THEN ', Forced Serialization' ELSE '' END +
4412 CASE WHEN is_key_lookup_expensive = 1 THEN ', Expensive Key Lookup' ELSE '' END +
4413 CASE WHEN is_remote_query_expensive = 1 THEN ', Expensive Remote Query' ELSE '' END +
4414 CASE WHEN trace_flags_session IS NOT NULL THEN ', Session Level Trace Flag(s) Enabled: ' + trace_flags_session ELSE '' END +
4415 CASE WHEN is_unused_grant = 1 THEN ', Unused Memory Grant' ELSE '' END +
4416 CASE WHEN function_count > 0 THEN ', Calls ' + CONVERT(VARCHAR(10), (SELECT SUM(b2.function_count) FROM ##BlitzCacheProcs AS b2 WHERE b2.SqlHandle = b.SqlHandle AND b2.QueryHash IS NOT NULL) ) + ' function(s)' ELSE '' END +
4417 CASE WHEN clr_function_count > 0 THEN ', Calls ' + CONVERT(VARCHAR(10), (SELECT SUM(b2.clr_function_count) FROM ##BlitzCacheProcs AS b2 WHERE b2.SqlHandle = b.SqlHandle AND b2.QueryHash IS NOT NULL) ) + ' CLR function(s)' ELSE '' END +
4418 CASE WHEN PlanCreationTimeHours <= 4 THEN ', Plan created last 4hrs' ELSE '' END +
4419 CASE WHEN is_table_variable = 1 THEN ', Table Variables' ELSE '' END +
4420 CASE WHEN no_stats_warning = 1 THEN ', Columns With No Statistics' ELSE '' END +
4421 CASE WHEN relop_warnings = 1 THEN ', Operator Warnings' ELSE '' END +
4422 CASE WHEN is_table_scan = 1 THEN ', Table Scans' ELSE '' END +
4423 CASE WHEN backwards_scan = 1 THEN ', Backwards Scans' ELSE '' END +
4424 CASE WHEN forced_index = 1 THEN ', Forced Indexes' ELSE '' END +
4425 CASE WHEN forced_seek = 1 THEN ', Forced Seeks' ELSE '' END +
4426 CASE WHEN forced_scan = 1 THEN ', Forced Scans' ELSE '' END +
4427 CASE WHEN columnstore_row_mode = 1 THEN ', ColumnStore Row Mode ' ELSE '' END +
4428 CASE WHEN is_computed_scalar = 1 THEN ', Computed Column UDF ' ELSE '' END +
4429 CASE WHEN is_sort_expensive = 1 THEN ', Expensive Sort' ELSE '' END +
4430 CASE WHEN is_computed_filter = 1 THEN ', Filter UDF' ELSE '' END +
4431 CASE WHEN index_ops >= 5 THEN ', >= 5 Indexes Modified' ELSE '' END +
4432 CASE WHEN is_row_level = 1 THEN ', Row Level Security' ELSE '' END +
4433 CASE WHEN is_spatial = 1 THEN ', Spatial Index' ELSE '' END +
4434 CASE WHEN index_dml = 1 THEN ', Index DML' ELSE '' END +
4435 CASE WHEN table_dml = 1 THEN ', Table DML' ELSE '' END +
4436 CASE WHEN low_cost_high_cpu = 1 THEN ', Low Cost High CPU' ELSE '' END +
4437 CASE WHEN long_running_low_cpu = 1 THEN + ', Long Running With Low CPU' ELSE '' END +
4438 CASE WHEN stale_stats = 1 THEN + ', Statistics used have > 100k modifications in the last 7 days' ELSE '' END +
4439 CASE WHEN is_adaptive = 1 THEN + ', Adaptive Joins' ELSE '' END +
4440 CASE WHEN is_spool_expensive = 1 THEN + ', Expensive Index Spool' ELSE '' END +
4441 CASE WHEN is_spool_more_rows = 1 THEN + ', Large Index Row Spool' ELSE '' END +
4442 CASE WHEN is_bad_estimate = 1 THEN + ', Row estimate mismatch' ELSE '' END +
4443 CASE WHEN is_paul_white_electric = 1 THEN ', SWITCH!' ELSE '' END +
4444 CASE WHEN is_row_goal = 1 THEN ', Row Goals' ELSE '' END +
4445 CASE WHEN is_big_spills = 1 THEN ', >500mb spills' ELSE '' END +
4446 CASE WHEN is_mstvf = 1 THEN ', MSTVFs' ELSE '' END +
4447 CASE WHEN is_mm_join = 1 THEN ', Many to Many Merge' ELSE '' END +
4448 CASE WHEN is_nonsargable = 1 THEN ', non-SARGables' ELSE '' END +
4449 CASE WHEN CompileTime > 5000 THEN ', Long Compile Time' ELSE '' END +
4450 CASE WHEN CompileCPU > 5000 THEN ', High Compile CPU' ELSE '' END +
4451 CASE WHEN CompileMemory > 1024 AND ((CompileMemory) / (1 * CASE WHEN MaxCompileMemory = 0 THEN 1 ELSE MaxCompileMemory END) * 100.) >= 10. THEN ', High Compile Memory' ELSE '' END
4452 , 3, 200000)
4453FROM ##BlitzCacheProcs b
4454WHERE SPID = @@SPID
4455AND QueryType LIKE 'Statement (parent%'
4456 )
4457UPDATE b
4458SET b.Warnings = s.Warnings
4459FROM ##BlitzCacheProcs AS b
4460JOIN statement_warnings s
4461ON b.SqlHandle = s.SqlHandle
4462WHERE QueryType LIKE 'Procedure or Function%'
4463AND SPID = @@SPID
4464OPTION (RECOMPILE);
4465
4466RAISERROR('Checking for plans with >128 levels of nesting', 0, 1) WITH NOWAIT;
4467WITH plan_handle AS (
4468SELECT b.PlanHandle
4469FROM ##BlitzCacheProcs b
4470 CROSS APPLY sys.dm_exec_text_query_plan(b.PlanHandle, 0, -1) tqp
4471 CROSS APPLY sys.dm_exec_query_plan(b.PlanHandle) qp
4472 WHERE tqp.encrypted = 0
4473 AND b.SPID = @@SPID
4474 AND (qp.query_plan IS NULL
4475 AND tqp.query_plan IS NOT NULL)
4476)
4477UPDATE b
4478SET Warnings = ISNULL('Your query plan is >128 levels of nested nodes, and can''t be converted to XML. Use SELECT * FROM sys.dm_exec_text_query_plan('+ CONVERT(VARCHAR(128), ph.PlanHandle, 1) + ', 0, -1) to get more information'
4479 , 'We couldn''t find a plan for this query. Possible reasons for this include dynamic SQL, RECOMPILE hints, and encrypted code.')
4480FROM ##BlitzCacheProcs b
4481LEFT JOIN plan_handle ph ON
4482b.PlanHandle = ph.PlanHandle
4483WHERE b.QueryPlan IS NULL
4484AND b.SPID = @@SPID
4485OPTION (RECOMPILE);
4486
4487RAISERROR('Checking for plans with no warnings', 0, 1) WITH NOWAIT;
4488UPDATE ##BlitzCacheProcs
4489SET Warnings = 'No warnings detected. ' + CASE @ExpertMode
4490 WHEN 0
4491 THEN ' Try running sp_BlitzCache with @ExpertMode = 1 to find more advanced problems.'
4492 ELSE ''
4493 END
4494WHERE Warnings = '' OR Warnings IS NULL
4495AND SPID = @@SPID
4496OPTION (RECOMPILE);
4497
4498
4499Results:
4500IF @OutputDatabaseName IS NOT NULL
4501 AND @OutputSchemaName IS NOT NULL
4502 AND @OutputTableName IS NOT NULL
4503BEGIN
4504 RAISERROR('Writing results to table.', 0, 1) WITH NOWAIT;
4505
4506 /* send results to a table */
4507 DECLARE @insert_sql NVARCHAR(MAX) = N'' ;
4508
4509 SET @insert_sql = 'USE '
4510 + @OutputDatabaseName
4511 + '; IF EXISTS(SELECT * FROM '
4512 + @OutputDatabaseName
4513 + '.INFORMATION_SCHEMA.SCHEMATA WHERE QUOTENAME(SCHEMA_NAME) = '''
4514 + @OutputSchemaName
4515 + ''') AND NOT EXISTS (SELECT * FROM '
4516 + @OutputDatabaseName
4517 + '.INFORMATION_SCHEMA.TABLES WHERE QUOTENAME(TABLE_SCHEMA) = '''
4518 + @OutputSchemaName + ''' AND QUOTENAME(TABLE_NAME) = '''
4519 + @OutputTableName + ''') CREATE TABLE '
4520 + @OutputSchemaName + '.'
4521 + @OutputTableName
4522 + N'(ID bigint NOT NULL IDENTITY(1,1),
4523 ServerName NVARCHAR(258),
4524 CheckDate DATETIMEOFFSET,
4525 Version NVARCHAR(258),
4526 QueryType NVARCHAR(258),
4527 Warnings varchar(max),
4528 DatabaseName sysname,
4529 SerialDesiredMemory float,
4530 SerialRequiredMemory float,
4531 AverageCPU bigint,
4532 TotalCPU bigint,
4533 PercentCPUByType money,
4534 CPUWeight money,
4535 AverageDuration bigint,
4536 TotalDuration bigint,
4537 DurationWeight money,
4538 PercentDurationByType money,
4539 AverageReads bigint,
4540 TotalReads bigint,
4541 ReadWeight money,
4542 PercentReadsByType money,
4543 AverageWrites bigint,
4544 TotalWrites bigint,
4545 WriteWeight money,
4546 PercentWritesByType money,
4547 ExecutionCount bigint,
4548 ExecutionWeight money,
4549 PercentExecutionsByType money,' + N'
4550 ExecutionsPerMinute money,
4551 PlanCreationTime datetime,
4552 PlanCreationTimeHours AS DATEDIFF(HOUR, PlanCreationTime, SYSDATETIME()),
4553 LastExecutionTime datetime,
4554 PlanHandle varbinary(64),
4555 [Remove Plan Handle From Cache] AS
4556 CASE WHEN [PlanHandle] IS NOT NULL
4557 THEN ''DBCC FREEPROCCACHE ('' + CONVERT(VARCHAR(128), [PlanHandle], 1) + '');''
4558 ELSE ''N/A'' END,
4559 SqlHandle varbinary(64),
4560 [Remove SQL Handle From Cache] AS
4561 CASE WHEN [SqlHandle] IS NOT NULL
4562 THEN ''DBCC FREEPROCCACHE ('' + CONVERT(VARCHAR(128), [SqlHandle], 1) + '');''
4563 ELSE ''N/A'' END,
4564 [SQL Handle More Info] AS
4565 CASE WHEN [SqlHandle] IS NOT NULL
4566 THEN ''EXEC sp_BlitzCache @OnlySqlHandles = '''''' + CONVERT(VARCHAR(128), [SqlHandle], 1) + ''''''; ''
4567 ELSE ''N/A'' END,
4568 QueryHash binary(8),
4569 [Query Hash More Info] AS
4570 CASE WHEN [QueryHash] IS NOT NULL
4571 THEN ''EXEC sp_BlitzCache @OnlyQueryHashes = '''''' + CONVERT(VARCHAR(32), [QueryHash], 1) + ''''''; ''
4572 ELSE ''N/A'' END,
4573 QueryPlanHash binary(8),
4574 StatementStartOffset int,
4575 StatementEndOffset int,
4576 MinReturnedRows bigint,
4577 MaxReturnedRows bigint,
4578 AverageReturnedRows money,
4579 TotalReturnedRows bigint,
4580 QueryText nvarchar(max),
4581 QueryPlan xml,
4582 NumberOfPlans int,
4583 NumberOfDistinctPlans int,
4584 MinGrantKB BIGINT,
4585 MaxGrantKB BIGINT,
4586 MinUsedGrantKB BIGINT,
4587 MaxUsedGrantKB BIGINT,
4588 PercentMemoryGrantUsed MONEY,
4589 AvgMaxMemoryGrant MONEY,
4590 MinSpills BIGINT,
4591 MaxSpills BIGINT,
4592 TotalSpills BIGINT,
4593 AvgSpills MONEY,
4594 QueryPlanCost FLOAT,
4595 CONSTRAINT [PK_' +CAST(NEWID() AS NCHAR(36)) + '] PRIMARY KEY CLUSTERED(ID))';
4596
4597 IF @Debug = 1
4598 BEGIN
4599 PRINT SUBSTRING(@insert_sql, 0, 4000);
4600 PRINT SUBSTRING(@insert_sql, 4000, 8000);
4601 PRINT SUBSTRING(@insert_sql, 8000, 12000);
4602 PRINT SUBSTRING(@insert_sql, 12000, 16000);
4603 PRINT SUBSTRING(@insert_sql, 16000, 20000);
4604 PRINT SUBSTRING(@insert_sql, 20000, 24000);
4605 PRINT SUBSTRING(@insert_sql, 24000, 28000);
4606 PRINT SUBSTRING(@insert_sql, 28000, 32000);
4607 PRINT SUBSTRING(@insert_sql, 32000, 36000);
4608 PRINT SUBSTRING(@insert_sql, 36000, 40000);
4609 END;
4610
4611 EXEC sp_executesql @insert_sql ;
4612
4613 IF @CheckDateOverride IS NULL
4614 BEGIN
4615 SET @CheckDateOverride = SYSDATETIMEOFFSET();
4616 END;
4617
4618
4619 SET @insert_sql = N' IF EXISTS(SELECT * FROM '
4620 + @OutputDatabaseName
4621 + N'.INFORMATION_SCHEMA.SCHEMATA WHERE QUOTENAME(SCHEMA_NAME) = '''
4622 + @OutputSchemaName + N''') '
4623 + N'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;'
4624 + 'INSERT '
4625 + @OutputDatabaseName + '.'
4626 + @OutputSchemaName + '.'
4627 + @OutputTableName
4628 + N' (ServerName, CheckDate, Version, QueryType, DatabaseName, AverageCPU, TotalCPU, PercentCPUByType, CPUWeight, AverageDuration, TotalDuration, DurationWeight, PercentDurationByType, AverageReads, TotalReads, ReadWeight, PercentReadsByType, '
4629 + N' AverageWrites, TotalWrites, WriteWeight, PercentWritesByType, ExecutionCount, ExecutionWeight, PercentExecutionsByType, '
4630 + N' ExecutionsPerMinute, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, QueryHash, StatementStartOffset, StatementEndOffset, MinReturnedRows, MaxReturnedRows, AverageReturnedRows, TotalReturnedRows, QueryText, QueryPlan, NumberOfPlans, NumberOfDistinctPlans, Warnings, '
4631 + N' SerialRequiredMemory, SerialDesiredMemory, MinGrantKB, MaxGrantKB, MinUsedGrantKB, MaxUsedGrantKB, PercentMemoryGrantUsed, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, QueryPlanCost ) '
4632 + N'SELECT TOP (@Top) '
4633 + QUOTENAME(CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(128)), N'''') + N', @CheckDateOverride, '
4634 + QUOTENAME(CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(128)), N'''') + ', '
4635 + N' QueryType, DatabaseName, AverageCPU, TotalCPU, PercentCPUByType, PercentCPU, AverageDuration, TotalDuration, PercentDuration, PercentDurationByType, AverageReads, TotalReads, PercentReads, PercentReadsByType, '
4636 + N' AverageWrites, TotalWrites, PercentWrites, PercentWritesByType, ExecutionCount, PercentExecutions, PercentExecutionsByType, '
4637 + N' ExecutionsPerMinute, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, QueryHash, StatementStartOffset, StatementEndOffset, MinReturnedRows, MaxReturnedRows, AverageReturnedRows, TotalReturnedRows, QueryText, QueryPlan, NumberOfPlans, NumberOfDistinctPlans, Warnings, '
4638 + N' SerialRequiredMemory, SerialDesiredMemory, MinGrantKB, MaxGrantKB, MinUsedGrantKB, MaxUsedGrantKB, PercentMemoryGrantUsed, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, QueryPlanCost '
4639 + N' FROM ##BlitzCacheProcs '
4640 + N' WHERE 1=1 ';
4641
4642 IF @MinimumExecutionCount IS NOT NULL
4643 BEGIN
4644 SET @insert_sql += N' AND ExecutionCount >= @MinimumExecutionCount ';
4645 END;
4646
4647 IF @MinutesBack IS NOT NULL
4648 BEGIN
4649 SET @insert_sql += N' AND LastExecutionTime >= DATEADD(MINUTE, @min_back, GETDATE() ) ';
4650 END;
4651
4652 SET @insert_sql += N' AND SPID = @@SPID ';
4653
4654 SELECT @insert_sql += N' ORDER BY ' + CASE @SortOrder WHEN 'cpu' THEN N' TotalCPU '
4655 WHEN N'reads' THEN N' TotalReads '
4656 WHEN N'writes' THEN N' TotalWrites '
4657 WHEN N'duration' THEN N' TotalDuration '
4658 WHEN N'executions' THEN N' ExecutionCount '
4659 WHEN N'compiles' THEN N' PlanCreationTime '
4660 WHEN N'memory grant' THEN N' MaxGrantKB'
4661 WHEN N'spills' THEN N' MaxSpills'
4662 WHEN N'avg cpu' THEN N' AverageCPU'
4663 WHEN N'avg reads' THEN N' AverageReads'
4664 WHEN N'avg writes' THEN N' AverageWrites'
4665 WHEN N'avg duration' THEN N' AverageDuration'
4666 WHEN N'avg executions' THEN N' ExecutionsPerMinute'
4667 WHEN N'avg memory grant' THEN N' AvgMaxMemoryGrant'
4668 WHEN 'avg spills' THEN N' AvgSpills'
4669 END + N' DESC ';
4670
4671 SET @insert_sql += N' OPTION (RECOMPILE) ; ';
4672
4673 IF @Debug = 1
4674 BEGIN
4675 PRINT SUBSTRING(@insert_sql, 0, 4000);
4676 PRINT SUBSTRING(@insert_sql, 4000, 8000);
4677 PRINT SUBSTRING(@insert_sql, 8000, 12000);
4678 PRINT SUBSTRING(@insert_sql, 12000, 16000);
4679 PRINT SUBSTRING(@insert_sql, 16000, 20000);
4680 PRINT SUBSTRING(@insert_sql, 20000, 24000);
4681 PRINT SUBSTRING(@insert_sql, 24000, 28000);
4682 PRINT SUBSTRING(@insert_sql, 28000, 32000);
4683 PRINT SUBSTRING(@insert_sql, 32000, 36000);
4684 PRINT SUBSTRING(@insert_sql, 36000, 40000);
4685 END;
4686
4687 EXEC sp_executesql @insert_sql, N'@Top INT, @min_duration INT, @min_back INT, @CheckDateOverride DATETIMEOFFSET, @MinimumExecutionCount INT', @Top, @DurationFilter_i, @MinutesBack, @CheckDateOverride, @MinimumExecutionCount;
4688
4689 RETURN;
4690END;
4691ELSE IF @ExportToExcel = 1
4692BEGIN
4693 RAISERROR('Displaying results with Excel formatting (no plans).', 0, 1) WITH NOWAIT;
4694
4695 /* excel output */
4696 UPDATE ##BlitzCacheProcs
4697 SET QueryText = SUBSTRING(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(QueryText)),' ','<>'),'><',''),'<>',' '), 1, 32000)
4698 OPTION(RECOMPILE);
4699
4700 SET @sql = N'
4701 SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
4702 SELECT TOP (@Top)
4703 DatabaseName AS [Database Name],
4704 QueryPlanCost AS [Cost],
4705 QueryText,
4706 QueryType AS [Query Type],
4707 Warnings,
4708 ExecutionCount,
4709 ExecutionsPerMinute AS [Executions / Minute],
4710 PercentExecutions AS [Execution Weight],
4711 PercentExecutionsByType AS [% Executions (Type)],
4712 SerialDesiredMemory AS [Serial Desired Memory],
4713 SerialRequiredMemory AS [Serial Required Memory],
4714 TotalCPU AS [Total CPU (ms)],
4715 AverageCPU AS [Avg CPU (ms)],
4716 PercentCPU AS [CPU Weight],
4717 PercentCPUByType AS [% CPU (Type)],
4718 TotalDuration AS [Total Duration (ms)],
4719 AverageDuration AS [Avg Duration (ms)],
4720 PercentDuration AS [Duration Weight],
4721 PercentDurationByType AS [% Duration (Type)],
4722 TotalReads AS [Total Reads],
4723 AverageReads AS [Average Reads],
4724 PercentReads AS [Read Weight],
4725 PercentReadsByType AS [% Reads (Type)],
4726 TotalWrites AS [Total Writes],
4727 AverageWrites AS [Average Writes],
4728 PercentWrites AS [Write Weight],
4729 PercentWritesByType AS [% Writes (Type)],
4730 TotalReturnedRows,
4731 AverageReturnedRows,
4732 MinReturnedRows,
4733 MaxReturnedRows,
4734 MinGrantKB,
4735 MaxGrantKB,
4736 MinUsedGrantKB,
4737 MaxUsedGrantKB,
4738 PercentMemoryGrantUsed,
4739 AvgMaxMemoryGrant,
4740 MinSpills,
4741 MaxSpills,
4742 TotalSpills,
4743 AvgSpills,
4744 NumberOfPlans,
4745 NumberOfDistinctPlans,
4746 PlanCreationTime AS [Created At],
4747 LastExecutionTime AS [Last Execution],
4748 StatementStartOffset,
4749 StatementEndOffset,
4750 PlanHandle AS [Plan Handle],
4751 SqlHandle AS [SQL Handle],
4752 QueryHash,
4753 QueryPlanHash,
4754 COALESCE(SetOptions, '''') AS [SET Options]
4755 FROM ##BlitzCacheProcs
4756 WHERE 1 = 1
4757 AND SPID = @@SPID ' + @nl;
4758
4759 IF @MinimumExecutionCount IS NOT NULL
4760 BEGIN
4761 SET @sql += N' AND ExecutionCount >= @minimumExecutionCount ';
4762 END;
4763
4764 IF @MinutesBack IS NOT NULL
4765 BEGIN
4766 SET @sql += N' AND LastExecutionTime >= DATEADD(MINUTE, @min_back, GETDATE() ) ';
4767 END;
4768
4769 SELECT @sql += N' ORDER BY ' + CASE @SortOrder WHEN N'cpu' THEN N' TotalCPU '
4770 WHEN N'reads' THEN N' TotalReads '
4771 WHEN N'writes' THEN N' TotalWrites '
4772 WHEN N'duration' THEN N' TotalDuration '
4773 WHEN N'executions' THEN N' ExecutionCount '
4774 WHEN N'compiles' THEN N' PlanCreationTime '
4775 WHEN N'memory grant' THEN N' MaxGrantKB'
4776 WHEN N'spills' THEN N' MaxSpills'
4777 WHEN N'avg cpu' THEN N' AverageCPU'
4778 WHEN N'avg reads' THEN N' AverageReads'
4779 WHEN N'avg writes' THEN N' AverageWrites'
4780 WHEN N'avg duration' THEN N' AverageDuration'
4781 WHEN N'avg executions' THEN N' ExecutionsPerMinute'
4782 WHEN N'avg memory grant' THEN N' AvgMaxMemoryGrant'
4783 WHEN N'avg spills' THEN N' AvgSpills'
4784 END + N' DESC ';
4785
4786 SET @sql += N' OPTION (RECOMPILE) ; ';
4787
4788 IF @Debug = 1
4789 BEGIN
4790 PRINT SUBSTRING(@sql, 0, 4000);
4791 PRINT SUBSTRING(@sql, 4000, 8000);
4792 PRINT SUBSTRING(@sql, 8000, 12000);
4793 PRINT SUBSTRING(@sql, 12000, 16000);
4794 PRINT SUBSTRING(@sql, 16000, 20000);
4795 PRINT SUBSTRING(@sql, 20000, 24000);
4796 PRINT SUBSTRING(@sql, 24000, 28000);
4797 PRINT SUBSTRING(@sql, 28000, 32000);
4798 PRINT SUBSTRING(@sql, 32000, 36000);
4799 PRINT SUBSTRING(@sql, 36000, 40000);
4800 END;
4801
4802 EXEC sp_executesql @sql, N'@Top INT, @min_duration INT, @min_back INT, @minimumExecutionCount INT', @Top, @DurationFilter_i, @MinutesBack, @MinimumExecutionCount;
4803END;
4804
4805
4806RAISERROR('Displaying analysis of plan cache.', 0, 1) WITH NOWAIT;
4807
4808DECLARE @columns NVARCHAR(MAX) = N'' ;
4809
4810IF @ExpertMode = 0
4811BEGIN
4812 RAISERROR(N'Returning ExpertMode = 0', 0, 1) WITH NOWAIT;
4813 SET @columns = N' DatabaseName AS [Database],
4814 QueryPlanCost AS [Cost],
4815 QueryText AS [Query Text],
4816 QueryType AS [Query Type],
4817 Warnings AS [Warnings],
4818 QueryPlan AS [Query Plan],
4819 missing_indexes AS [Missing Indexes],
4820 implicit_conversion_info AS [Implicit Conversion Info],
4821 cached_execution_parameters AS [Cached Execution Parameters],
4822 REPLACE(CONVERT(NVARCHAR(30), CAST((ExecutionCount) AS MONEY), 1), N''.00'', N'''') AS [# Executions],
4823 REPLACE(CONVERT(NVARCHAR(30), CAST((ExecutionsPerMinute) AS MONEY), 1), N''.00'', N'''') AS [Executions / Minute],
4824 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentExecutions) AS MONEY), 1), N''.00'', N'''') AS [Execution Weight],
4825 REPLACE(CONVERT(NVARCHAR(30), CAST((TotalCPU) AS MONEY), 1), N''.00'', N'''') AS [Total CPU (ms)],
4826 REPLACE(CONVERT(NVARCHAR(30), CAST((AverageCPU) AS MONEY), 1), N''.00'', N'''') AS [Avg CPU (ms)],
4827 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentCPU) AS MONEY), 1), N''.00'', N'''') AS [CPU Weight],
4828 REPLACE(CONVERT(NVARCHAR(30), CAST((TotalDuration) AS MONEY), 1), N''.00'', N'''') AS [Total Duration (ms)],
4829 REPLACE(CONVERT(NVARCHAR(30), CAST((AverageDuration) AS MONEY), 1), N''.00'', N'''') AS [Avg Duration (ms)],
4830 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentDuration) AS MONEY), 1), N''.00'', N'''') AS [Duration Weight],
4831 REPLACE(CONVERT(NVARCHAR(30), CAST((TotalReads) AS MONEY), 1), N''.00'', N'''') AS [Total Reads],
4832 REPLACE(CONVERT(NVARCHAR(30), CAST((AverageReads) AS MONEY), 1), N''.00'', N'''') AS [Avg Reads],
4833 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentReads) AS MONEY), 1), N''.00'', N'''') AS [Read Weight],
4834 REPLACE(CONVERT(NVARCHAR(30), CAST((TotalWrites) AS MONEY), 1), N''.00'', N'''') AS [Total Writes],
4835 REPLACE(CONVERT(NVARCHAR(30), CAST((AverageWrites) AS MONEY), 1), N''.00'', N'''') AS [Avg Writes],
4836 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentWrites) AS MONEY), 1), N''.00'', N'''') AS [Write Weight],
4837 REPLACE(CONVERT(NVARCHAR(30), CAST((AverageReturnedRows) AS MONEY), 1), N''.00'', N'''') AS [Average Rows],
4838 REPLACE(CONVERT(NVARCHAR(30), CAST((MinGrantKB) AS MONEY), 1), N''.00'', N'''') AS [Minimum Memory Grant KB],
4839 REPLACE(CONVERT(NVARCHAR(30), CAST((MaxGrantKB) AS MONEY), 1), N''.00'', N'''') AS [Maximum Memory Grant KB],
4840 REPLACE(CONVERT(NVARCHAR(30), CAST((MinUsedGrantKB) AS MONEY), 1), N''.00'', N'''') AS [Minimum Used Grant KB],
4841 REPLACE(CONVERT(NVARCHAR(30), CAST((MaxUsedGrantKB) AS MONEY), 1), N''.00'', N'''') AS [Maximum Used Grant KB],
4842 REPLACE(CONVERT(NVARCHAR(30), CAST((AvgMaxMemoryGrant) AS MONEY), 1), N''.00'', N'''') AS [Average Max Memory Grant],
4843 REPLACE(CONVERT(NVARCHAR(30), CAST((MinSpills) AS MONEY), 1), N''.00'', N'''') AS [Min Spills],
4844 REPLACE(CONVERT(NVARCHAR(30), CAST((MaxSpills) AS MONEY), 1), N''.00'', N'''') AS [Max Spills],
4845 REPLACE(CONVERT(NVARCHAR(30), CAST((TotalSpills) AS MONEY), 1), N''.00'', N'''') AS [Total Spills],
4846 REPLACE(CONVERT(NVARCHAR(30), CAST((AvgSpills) AS MONEY), 1), N''.00'', N'''') AS [Avg Spills],
4847 PlanCreationTime AS [Created At],
4848 LastExecutionTime AS [Last Execution],
4849 PlanHandle AS [Plan Handle],
4850 SqlHandle AS [SQL Handle],
4851 COALESCE(SetOptions, '''') AS [SET Options] ';
4852END;
4853ELSE
4854BEGIN
4855 SET @columns = N' DatabaseName AS [Database],
4856 QueryPlanCost AS [Cost],
4857 QueryText AS [Query Text],
4858 QueryType AS [Query Type],
4859 Warnings AS [Warnings],
4860 QueryPlan AS [Query Plan],
4861 missing_indexes AS [Missing Indexes],
4862 implicit_conversion_info AS [Implicit Conversion Info],
4863 cached_execution_parameters AS [Cached Execution Parameters], ' + @nl;
4864
4865 IF @ExpertMode = 2 /* Opserver */
4866 BEGIN
4867 RAISERROR(N'Returning Expert Mode = 2', 0, 1) WITH NOWAIT;
4868 SET @columns += N'
4869 SUBSTRING(
4870 CASE WHEN warning_no_join_predicate = 1 THEN '', 20'' ELSE '''' END +
4871 CASE WHEN compile_timeout = 1 THEN '', 18'' ELSE '''' END +
4872 CASE WHEN compile_memory_limit_exceeded = 1 THEN '', 19'' ELSE '''' END +
4873 CASE WHEN busy_loops = 1 THEN '', 16'' ELSE '''' END +
4874 CASE WHEN is_forced_plan = 1 THEN '', 3'' ELSE '''' END +
4875 CASE WHEN is_forced_parameterized > 0 THEN '', 5'' ELSE '''' END +
4876 CASE WHEN unparameterized_query = 1 THEN '', 23'' ELSE '''' END +
4877 CASE WHEN missing_index_count > 0 THEN '', 10'' ELSE '''' END +
4878 CASE WHEN unmatched_index_count > 0 THEN '', 22'' ELSE '''' END +
4879 CASE WHEN is_cursor = 1 THEN '', 4'' ELSE '''' END +
4880 CASE WHEN is_parallel = 1 THEN '', 6'' ELSE '''' END +
4881 CASE WHEN near_parallel = 1 THEN '', 7'' ELSE '''' END +
4882 CASE WHEN frequent_execution = 1 THEN '', 1'' ELSE '''' END +
4883 CASE WHEN plan_warnings = 1 THEN '', 8'' ELSE '''' END +
4884 CASE WHEN parameter_sniffing = 1 THEN '', 2'' ELSE '''' END +
4885 CASE WHEN long_running = 1 THEN '', 9'' ELSE '''' END +
4886 CASE WHEN downlevel_estimator = 1 THEN '', 13'' ELSE '''' END +
4887 CASE WHEN implicit_conversions = 1 THEN '', 14'' ELSE '''' END +
4888 CASE WHEN tvf_join = 1 THEN '', 17'' ELSE '''' END +
4889 CASE WHEN plan_multiple_plans = 1 THEN '', 21'' ELSE '''' END +
4890 CASE WHEN unmatched_index_count > 0 THEN '', 22'' ELSE '''' END +
4891 CASE WHEN is_trivial = 1 THEN '', 24'' ELSE '''' END +
4892 CASE WHEN is_forced_serial = 1 THEN '', 25'' ELSE '''' END +
4893 CASE WHEN is_key_lookup_expensive = 1 THEN '', 26'' ELSE '''' END +
4894 CASE WHEN is_remote_query_expensive = 1 THEN '', 28'' ELSE '''' END +
4895 CASE WHEN trace_flags_session IS NOT NULL THEN '', 29'' ELSE '''' END +
4896 CASE WHEN is_unused_grant = 1 THEN '', 30'' ELSE '''' END +
4897 CASE WHEN function_count > 0 THEN '', 31'' ELSE '''' END +
4898 CASE WHEN clr_function_count > 0 THEN '', 32'' ELSE '''' END +
4899 CASE WHEN PlanCreationTimeHours <= 4 THEN '', 33'' ELSE '''' END +
4900 CASE WHEN is_table_variable = 1 THEN '', 34'' ELSE '''' END +
4901 CASE WHEN no_stats_warning = 1 THEN '', 35'' ELSE '''' END +
4902 CASE WHEN relop_warnings = 1 THEN '', 36'' ELSE '''' END +
4903 CASE WHEN is_table_scan = 1 THEN '', 37'' ELSE '''' END +
4904 CASE WHEN backwards_scan = 1 THEN '', 38'' ELSE '''' END +
4905 CASE WHEN forced_index = 1 THEN '', 39'' ELSE '''' END +
4906 CASE WHEN forced_seek = 1 OR forced_scan = 1 THEN '', 40'' ELSE '''' END +
4907 CASE WHEN columnstore_row_mode = 1 THEN '', 41'' ELSE '''' END +
4908 CASE WHEN is_computed_scalar = 1 THEN '', 42'' ELSE '''' END +
4909 CASE WHEN is_sort_expensive = 1 THEN '', 43'' ELSE '''' END +
4910 CASE WHEN is_computed_filter = 1 THEN '', 44'' ELSE '''' END +
4911 CASE WHEN index_ops >= 5 THEN '', 45'' ELSE '''' END +
4912 CASE WHEN is_row_level = 1 THEN '', 46'' ELSE '''' END +
4913 CASE WHEN is_spatial = 1 THEN '', 47'' ELSE '''' END +
4914 CASE WHEN index_dml = 1 THEN '', 48'' ELSE '''' END +
4915 CASE WHEN table_dml = 1 THEN '', 49'' ELSE '''' END +
4916 CASE WHEN long_running_low_cpu = 1 THEN '', 50'' ELSE '''' END +
4917 CASE WHEN low_cost_high_cpu = 1 THEN '', 51'' ELSE '''' END +
4918 CASE WHEN stale_stats = 1 THEN '', 52'' ELSE '''' END +
4919 CASE WHEN is_adaptive = 1 THEN '', 53'' ELSE '''' END +
4920 CASE WHEN is_spool_expensive = 1 THEN + '', 54'' ELSE '''' END +
4921 CASE WHEN is_spool_more_rows = 1 THEN + '', 55'' ELSE '''' END +
4922 CASE WHEN is_bad_estimate = 1 THEN + '', 56'' ELSE '''' END +
4923 CASE WHEN is_paul_white_electric = 1 THEN '', 57'' ELSE '''' END +
4924 CASE WHEN is_row_goal = 1 THEN '', 58'' ELSE '''' END +
4925 CASE WHEN is_big_spills = 1 THEN '', 59'' ELSE '''' END +
4926 CASE WHEN is_mstvf = 1 THEN '', 60'' ELSE '''' END +
4927 CASE WHEN is_mm_join = 1 THEN '', 61'' ELSE '''' END +
4928 CASE WHEN is_nonsargable = 1 THEN '', 62'' ELSE '''' END +
4929 CASE WHEN CompileTime > 5000 THEN '', 63 '' ELSE '''' END +
4930 CASE WHEN CompileCPU > 5000 THEN '', 64 '' ELSE '''' END +
4931 CASE WHEN CompileMemory > 1024 AND ((CompileMemory) / (1 * CASE WHEN MaxCompileMemory = 0 THEN 1 ELSE MaxCompileMemory END) * 100.) >= 10. THEN '', 65 '' ELSE '''' END
4932 , 3, 200000) AS opserver_warning , ' + @nl ;
4933 END;
4934
4935 SET @columns += N'
4936 REPLACE(CONVERT(NVARCHAR(30), CAST((ExecutionCount) AS MONEY), 1), N''.00'', N'''') AS [# Executions],
4937 REPLACE(CONVERT(NVARCHAR(30), CAST((ExecutionsPerMinute) AS MONEY), 1), N''.00'', N'''') AS [Executions / Minute],
4938 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentExecutions) AS MONEY), 1), N''.00'', N'''') AS [Execution Weight],
4939 REPLACE(CONVERT(NVARCHAR(30), CAST((SerialDesiredMemory) AS MONEY), 1), N''.00'', N'''') AS [Serial Desired Memory],
4940 REPLACE(CONVERT(NVARCHAR(30), CAST((SerialRequiredMemory) AS MONEY), 1), N''.00'', N'''') AS [Serial Required Memory],
4941 REPLACE(CONVERT(NVARCHAR(30), CAST((TotalCPU) AS MONEY), 1), N''.00'', N'''') AS [Total CPU (ms)],
4942 REPLACE(CONVERT(NVARCHAR(30), CAST((AverageCPU) AS MONEY), 1), N''.00'', N'''') AS [Avg CPU (ms)],
4943 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentCPU) AS MONEY), 1), N''.00'', N'''') AS [CPU Weight],
4944 REPLACE(CONVERT(NVARCHAR(30), CAST((TotalDuration) AS MONEY), 1), N''.00'', N'''') AS [Total Duration (ms)],
4945 REPLACE(CONVERT(NVARCHAR(30), CAST((AverageDuration) AS MONEY), 1), N''.00'', N'''') AS [Avg Duration (ms)],
4946 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentDuration) AS MONEY), 1), N''.00'', N'''') AS [Duration Weight],
4947 REPLACE(CONVERT(NVARCHAR(30), CAST((TotalReads) AS MONEY), 1), N''.00'', N'''') AS [Total Reads],
4948 REPLACE(CONVERT(NVARCHAR(30), CAST((AverageReads) AS MONEY), 1), N''.00'', N'''') AS [Average Reads],
4949 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentReads) AS MONEY), 1), N''.00'', N'''') AS [Read Weight],
4950 REPLACE(CONVERT(NVARCHAR(30), CAST((TotalWrites) AS MONEY), 1), N''.00'', N'''') AS [Total Writes],
4951 REPLACE(CONVERT(NVARCHAR(30), CAST((AverageWrites) AS MONEY), 1), N''.00'', N'''') AS [Average Writes],
4952 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentWrites) AS MONEY), 1), N''.00'', N'''') AS [Write Weight],
4953 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentExecutionsByType) AS MONEY), 1), N''.00'', N'''') AS [% Executions (Type)],
4954 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentCPUByType) AS MONEY), 1), N''.00'', N'''') AS [% CPU (Type)],
4955 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentDurationByType) AS MONEY), 1), N''.00'', N'''') AS [% Duration (Type)],
4956 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentReadsByType) AS MONEY), 1), N''.00'', N'''') AS [% Reads (Type)],
4957 REPLACE(CONVERT(NVARCHAR(30), CAST((PercentWritesByType) AS MONEY), 1), N''.00'', N'''') AS [% Writes (Type)],
4958 REPLACE(CONVERT(NVARCHAR(30), CAST((TotalReturnedRows) AS MONEY), 1), N''.00'', N'''') AS [Total Rows],
4959 REPLACE(CONVERT(NVARCHAR(30), CAST((AverageReturnedRows) AS MONEY), 1), N''.00'', N'''') AS [Avg Rows],
4960 REPLACE(CONVERT(NVARCHAR(30), CAST((MinReturnedRows) AS MONEY), 1), N''.00'', N'''') AS [Min Rows],
4961 REPLACE(CONVERT(NVARCHAR(30), CAST((MaxReturnedRows) AS MONEY), 1), N''.00'', N'''') AS [Max Rows],
4962 REPLACE(CONVERT(NVARCHAR(30), CAST((MinGrantKB) AS MONEY), 1), N''.00'', N'''') AS [Minimum Memory Grant KB],
4963 REPLACE(CONVERT(NVARCHAR(30), CAST((MaxGrantKB) AS MONEY), 1), N''.00'', N'''') AS [Maximum Memory Grant KB],
4964 REPLACE(CONVERT(NVARCHAR(30), CAST((MinUsedGrantKB) AS MONEY), 1), N''.00'', N'''') AS [Minimum Used Grant KB],
4965 REPLACE(CONVERT(NVARCHAR(30), CAST((MaxUsedGrantKB) AS MONEY), 1), N''.00'', N'''') AS [Maximum Used Grant KB],
4966 REPLACE(CONVERT(NVARCHAR(30), CAST((AvgMaxMemoryGrant) AS MONEY), 1), N''.00'', N'''') AS [Average Max Memory Grant],
4967 REPLACE(CONVERT(NVARCHAR(30), CAST((MinSpills) AS MONEY), 1), N''.00'', N'''') AS [Min Spills],
4968 REPLACE(CONVERT(NVARCHAR(30), CAST((MaxSpills) AS MONEY), 1), N''.00'', N'''') AS [Max Spills],
4969 REPLACE(CONVERT(NVARCHAR(30), CAST((TotalSpills) AS MONEY), 1), N''.00'', N'''') AS [Total Spills],
4970 REPLACE(CONVERT(NVARCHAR(30), CAST((AvgSpills) AS MONEY), 1), N''.00'', N'''') AS [Avg Spills],
4971 REPLACE(CONVERT(NVARCHAR(30), CAST((NumberOfPlans) AS MONEY), 1), N''.00'', N'''') AS [# Plans],
4972 REPLACE(CONVERT(NVARCHAR(30), CAST((NumberOfDistinctPlans) AS MONEY), 1), N''.00'', N'''') AS [# Distinct Plans],
4973 PlanCreationTime AS [Created At],
4974 LastExecutionTime AS [Last Execution],
4975 REPLACE(CONVERT(NVARCHAR(30), CAST((CachedPlanSize) AS MONEY), 1), N''.00'', N'''') AS [Cached Plan Size (KB)],
4976 REPLACE(CONVERT(NVARCHAR(30), CAST((CompileTime) AS MONEY), 1), N''.00'', N'''') AS [Compile Time (ms)],
4977 REPLACE(CONVERT(NVARCHAR(30), CAST((CompileCPU) AS MONEY), 1), N''.00'', N'''') AS [Compile CPU (ms)],
4978 REPLACE(CONVERT(NVARCHAR(30), CAST((CompileMemory) AS MONEY), 1), N''.00'', N'''') AS [Compile memory (KB)],
4979 COALESCE(SetOptions, '''') AS [SET Options],
4980 PlanHandle AS [Plan Handle],
4981 SqlHandle AS [SQL Handle],
4982 [SQL Handle More Info],
4983 QueryHash AS [Query Hash],
4984 [Query Hash More Info],
4985 QueryPlanHash AS [Query Plan Hash],
4986 StatementStartOffset,
4987 StatementEndOffset,
4988 [Remove Plan Handle From Cache],
4989 [Remove SQL Handle From Cache]';
4990END;
4991
4992
4993
4994SET @sql = N'
4995SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
4996SELECT TOP (@Top) ' + @columns + @nl + N'
4997FROM ##BlitzCacheProcs
4998WHERE SPID = @spid ' + @nl;
4999
5000IF @MinimumExecutionCount IS NOT NULL
5001 BEGIN
5002 SET @sql += N' AND ExecutionCount >= @minimumExecutionCount ' + @nl;
5003 END;
5004
5005IF @MinutesBack IS NOT NULL
5006 BEGIN
5007 SET @sql += N' AND LastExecutionTime >= DATEADD(MINUTE, @min_back, GETDATE() ) ' + @nl;
5008 END;
5009
5010SELECT @sql += N' ORDER BY ' + CASE @SortOrder WHEN N'cpu' THEN N' TotalCPU '
5011 WHEN N'reads' THEN N' TotalReads '
5012 WHEN N'writes' THEN N' TotalWrites '
5013 WHEN N'duration' THEN N' TotalDuration '
5014 WHEN N'executions' THEN N' ExecutionCount '
5015 WHEN N'compiles' THEN N' PlanCreationTime '
5016 WHEN N'memory grant' THEN N' MaxGrantKB'
5017 WHEN N'spills' THEN N' MaxSpills'
5018 WHEN N'avg cpu' THEN N' AverageCPU'
5019 WHEN N'avg reads' THEN N' AverageReads'
5020 WHEN N'avg writes' THEN N' AverageWrites'
5021 WHEN N'avg duration' THEN N' AverageDuration'
5022 WHEN N'avg executions' THEN N' ExecutionsPerMinute'
5023 WHEN N'avg memory grant' THEN N' AvgMaxMemoryGrant'
5024 WHEN N'avg spills' THEN N' AvgSpills'
5025 END + N' DESC ';
5026SET @sql += N' OPTION (RECOMPILE) ; ';
5027
5028IF @Debug = 1
5029 BEGIN
5030 PRINT SUBSTRING(@sql, 0, 4000);
5031 PRINT SUBSTRING(@sql, 4000, 8000);
5032 PRINT SUBSTRING(@sql, 8000, 12000);
5033 PRINT SUBSTRING(@sql, 12000, 16000);
5034 PRINT SUBSTRING(@sql, 16000, 20000);
5035 PRINT SUBSTRING(@sql, 20000, 24000);
5036 PRINT SUBSTRING(@sql, 24000, 28000);
5037 PRINT SUBSTRING(@sql, 28000, 32000);
5038 PRINT SUBSTRING(@sql, 32000, 36000);
5039 PRINT SUBSTRING(@sql, 36000, 40000);
5040 END;
5041
5042EXEC sp_executesql @sql, N'@Top INT, @spid INT, @minimumExecutionCount INT, @min_back INT', @Top, @@SPID, @MinimumExecutionCount, @MinutesBack;
5043
5044IF @HideSummary = 0 AND @ExportToExcel = 0
5045BEGIN
5046 IF @Reanalyze = 0
5047 BEGIN
5048 RAISERROR('Building query plan summary data.', 0, 1) WITH NOWAIT;
5049
5050 /* Build summary data */
5051 IF EXISTS (SELECT 1/0
5052 FROM ##BlitzCacheProcs
5053 WHERE frequent_execution = 1
5054 AND SPID = @@SPID)
5055 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5056 VALUES (@@SPID,
5057 1,
5058 100,
5059 'Execution Pattern',
5060 'Frequently Executed Queries',
5061 'http://brentozar.com/blitzcache/frequently-executed-queries/',
5062 'Queries are being executed more than '
5063 + CAST (@execution_threshold AS VARCHAR(5))
5064 + ' times per minute. This can put additional load on the server, even when queries are lightweight.') ;
5065
5066 IF EXISTS (SELECT 1/0
5067 FROM ##BlitzCacheProcs
5068 WHERE parameter_sniffing = 1
5069 AND SPID = @@SPID)
5070 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5071 VALUES (@@SPID,
5072 2,
5073 50,
5074 'Parameterization',
5075 'Parameter Sniffing',
5076 'http://brentozar.com/blitzcache/parameter-sniffing/',
5077 'There are signs of parameter sniffing (wide variance in rows return or time to execute). Investigate query patterns and tune code appropriately.') ;
5078
5079 /* Forced execution plans */
5080 IF EXISTS (SELECT 1/0
5081 FROM ##BlitzCacheProcs
5082 WHERE is_forced_plan = 1
5083 AND SPID = @@SPID)
5084 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5085 VALUES (@@SPID,
5086 3,
5087 50,
5088 'Parameterization',
5089 'Forced Plans',
5090 'http://brentozar.com/blitzcache/forced-plans/',
5091 'Execution plans have been compiled with forced plans, either through FORCEPLAN, plan guides, or forced parameterization. This will make general tuning efforts less effective.');
5092
5093 IF EXISTS (SELECT 1/0
5094 FROM ##BlitzCacheProcs
5095 WHERE is_cursor = 1
5096 AND SPID = @@SPID)
5097 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5098 VALUES (@@SPID,
5099 4,
5100 200,
5101 'Cursors',
5102 'Cursors',
5103 'http://brentozar.com/blitzcache/cursors-found-slow-queries/',
5104 'There are cursors in the plan cache. This is neither good nor bad, but it is a thing. Cursors are weird in SQL Server.');
5105
5106 IF EXISTS (SELECT 1/0
5107 FROM ##BlitzCacheProcs
5108 WHERE is_cursor = 1
5109 AND is_optimistic_cursor = 1
5110 AND SPID = @@SPID)
5111 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5112 VALUES (@@SPID,
5113 4,
5114 200,
5115 'Cursors',
5116 'Optimistic Cursors',
5117 'http://brentozar.com/blitzcache/cursors-found-slow-queries/',
5118 'There are optimistic cursors in the plan cache, which can harm performance.');
5119
5120 IF EXISTS (SELECT 1/0
5121 FROM ##BlitzCacheProcs
5122 WHERE is_cursor = 1
5123 AND is_forward_only_cursor = 0
5124 AND SPID = @@SPID)
5125 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5126 VALUES (@@SPID,
5127 4,
5128 200,
5129 'Cursors',
5130 'Non-forward Only Cursors',
5131 'http://brentozar.com/blitzcache/cursors-found-slow-queries/',
5132 'There are non-forward only cursors in the plan cache, which can harm performance.');
5133
5134 IF EXISTS (SELECT 1/0
5135 FROM ##BlitzCacheProcs
5136 WHERE is_cursor = 1
5137 AND is_cursor_dynamic = 1
5138 AND SPID = @@SPID)
5139 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5140 VALUES (@@SPID,
5141 4,
5142 200,
5143 'Cursors',
5144 'Dynamic Cursors',
5145 'http://brentozar.com/blitzcache/cursors-found-slow-queries/',
5146 'Dynamic Cursors inhibit parallelism!.');
5147
5148 IF EXISTS (SELECT 1/0
5149 FROM ##BlitzCacheProcs
5150 WHERE is_cursor = 1
5151 AND is_fast_forward_cursor = 1
5152 AND SPID = @@SPID)
5153 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5154 VALUES (@@SPID,
5155 4,
5156 200,
5157 'Cursors',
5158 'Fast Forward Cursors',
5159 'http://brentozar.com/blitzcache/cursors-found-slow-queries/',
5160 'Fast forward cursors inhibit parallelism!.');
5161
5162 IF EXISTS (SELECT 1/0
5163 FROM ##BlitzCacheProcs
5164 WHERE is_forced_parameterized = 1
5165 AND SPID = @@SPID)
5166 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5167 VALUES (@@SPID,
5168 5,
5169 50,
5170 'Parameterization',
5171 'Forced Parameterization',
5172 'http://brentozar.com/blitzcache/forced-parameterization/',
5173 'Execution plans have been compiled with forced parameterization.') ;
5174
5175 IF EXISTS (SELECT 1/0
5176 FROM ##BlitzCacheProcs p
5177 WHERE p.is_parallel = 1
5178 AND SPID = @@SPID)
5179 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5180 VALUES (@@SPID,
5181 6,
5182 200,
5183 'Execution Plans',
5184 'Parallelism',
5185 'http://brentozar.com/blitzcache/parallel-plans-detected/',
5186 'Parallel plans detected. These warrant investigation, but are neither good nor bad.') ;
5187
5188 IF EXISTS (SELECT 1/0
5189 FROM ##BlitzCacheProcs p
5190 WHERE near_parallel = 1
5191 AND SPID = @@SPID)
5192 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5193 VALUES (@@SPID,
5194 7,
5195 200,
5196 'Execution Plans',
5197 'Nearly Parallel',
5198 'http://brentozar.com/blitzcache/query-cost-near-cost-threshold-parallelism/',
5199 'Queries near the cost threshold for parallelism. These may go parallel when you least expect it.') ;
5200
5201 IF EXISTS (SELECT 1/0
5202 FROM ##BlitzCacheProcs p
5203 WHERE plan_warnings = 1
5204 AND SPID = @@SPID)
5205 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5206 VALUES (@@SPID,
5207 8,
5208 50,
5209 'Execution Plans',
5210 'Query Plan Warnings',
5211 'http://brentozar.com/blitzcache/query-plan-warnings/',
5212 'Warnings detected in execution plans. SQL Server is telling you that something bad is going on that requires your attention.') ;
5213
5214 IF EXISTS (SELECT 1/0
5215 FROM ##BlitzCacheProcs p
5216 WHERE long_running = 1
5217 AND SPID = @@SPID)
5218 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5219 VALUES (@@SPID,
5220 9,
5221 50,
5222 'Performance',
5223 'Long Running Queries',
5224 'http://brentozar.com/blitzcache/long-running-queries/',
5225 'Long running queries have been found. These are queries with an average duration longer than '
5226 + CAST(@long_running_query_warning_seconds / 1000 / 1000 AS VARCHAR(5))
5227 + ' second(s). These queries should be investigated for additional tuning options.') ;
5228
5229 IF EXISTS (SELECT 1/0
5230 FROM ##BlitzCacheProcs p
5231 WHERE p.missing_index_count > 0
5232 AND SPID = @@SPID)
5233 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5234 VALUES (@@SPID,
5235 10,
5236 50,
5237 'Performance',
5238 'Missing Index Request',
5239 'http://brentozar.com/blitzcache/missing-index-request/',
5240 'Queries found with missing indexes.');
5241
5242 IF EXISTS (SELECT 1/0
5243 FROM ##BlitzCacheProcs p
5244 WHERE p.downlevel_estimator = 1
5245 AND SPID = @@SPID)
5246 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5247 VALUES (@@SPID,
5248 13,
5249 200,
5250 'Cardinality',
5251 'Legacy Cardinality Estimator in Use',
5252 'http://brentozar.com/blitzcache/legacy-cardinality-estimator/',
5253 'A legacy cardinality estimator is being used by one or more queries. Investigate whether you need to be using this cardinality estimator. This may be caused by compatibility levels, global trace flags, or query level trace flags.');
5254
5255 IF EXISTS (SELECT 1/0
5256 FROM ##BlitzCacheProcs p
5257 WHERE implicit_conversions = 1
5258 AND SPID = @@SPID)
5259 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5260 VALUES (@@SPID,
5261 14,
5262 50,
5263 'Performance',
5264 'Implicit Conversions',
5265 'http://brentozar.com/go/implicit',
5266 'One or more queries are comparing two fields that are not of the same data type.') ;
5267
5268 IF EXISTS (SELECT 1/0
5269 FROM ##BlitzCacheProcs
5270 WHERE busy_loops = 1
5271 AND SPID = @@SPID)
5272 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5273 VALUES (@@SPID,
5274 16,
5275 100,
5276 'Performance',
5277 'Busy Loops',
5278 'http://brentozar.com/blitzcache/busy-loops/',
5279 'Operations have been found that are executed 100 times more often than the number of rows returned by each iteration. This is an indicator that something is off in query execution.');
5280
5281 IF EXISTS (SELECT 1/0
5282 FROM ##BlitzCacheProcs
5283 WHERE tvf_join = 1
5284 AND SPID = @@SPID)
5285 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5286 VALUES (@@SPID,
5287 17,
5288 50,
5289 'Performance',
5290 'Joining to table valued functions',
5291 'http://brentozar.com/blitzcache/tvf-join/',
5292 'Execution plans have been found that join to table valued functions (TVFs). TVFs produce inaccurate estimates of the number of rows returned and can lead to any number of query plan problems.');
5293
5294 IF EXISTS (SELECT 1/0
5295 FROM ##BlitzCacheProcs
5296 WHERE compile_timeout = 1
5297 AND SPID = @@SPID)
5298 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5299 VALUES (@@SPID,
5300 18,
5301 50,
5302 'Execution Plans',
5303 'Compilation timeout',
5304 'http://brentozar.com/blitzcache/compilation-timeout/',
5305 'Query compilation timed out for one or more queries. SQL Server did not find a plan that meets acceptable performance criteria in the time allotted so the best guess was returned. There is a very good chance that this plan isn''t even below average - it''s probably terrible.');
5306
5307 IF EXISTS (SELECT 1/0
5308 FROM ##BlitzCacheProcs
5309 WHERE compile_memory_limit_exceeded = 1
5310 AND SPID = @@SPID)
5311 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5312 VALUES (@@SPID,
5313 19,
5314 50,
5315 'Execution Plans',
5316 'Compilation memory limit exceeded',
5317 'http://brentozar.com/blitzcache/compile-memory-limit-exceeded/',
5318 'The optimizer has a limited amount of memory available. One or more queries are complex enough that SQL Server was unable to allocate enough memory to fully optimize the query. A best fit plan was found, and it''s probably terrible.');
5319
5320 IF EXISTS (SELECT 1/0
5321 FROM ##BlitzCacheProcs
5322 WHERE warning_no_join_predicate = 1
5323 AND SPID = @@SPID)
5324 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5325 VALUES (@@SPID,
5326 20,
5327 50,
5328 'Execution Plans',
5329 'No join predicate',
5330 'http://brentozar.com/blitzcache/no-join-predicate/',
5331 'Operators in a query have no join predicate. This means that all rows from one table will be matched with all rows from anther table producing a Cartesian product. That''s a whole lot of rows. This may be your goal, but it''s important to investigate why this is happening.');
5332
5333 IF EXISTS (SELECT 1/0
5334 FROM ##BlitzCacheProcs
5335 WHERE plan_multiple_plans = 1
5336 AND SPID = @@SPID)
5337 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5338 VALUES (@@SPID,
5339 21,
5340 200,
5341 'Execution Plans',
5342 'Multiple execution plans',
5343 'http://brentozar.com/blitzcache/multiple-plans/',
5344 'Queries exist with multiple execution plans (as determined by query_plan_hash). Investigate possible ways to parameterize these queries or otherwise reduce the plan count.');
5345
5346 IF EXISTS (SELECT 1/0
5347 FROM ##BlitzCacheProcs
5348 WHERE unmatched_index_count > 0
5349 AND SPID = @@SPID)
5350 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5351 VALUES (@@SPID,
5352 22,
5353 100,
5354 'Performance',
5355 'Unmatched indexes',
5356 'http://brentozar.com/blitzcache/unmatched-indexes',
5357 'An index could have been used, but SQL Server chose not to use it - likely due to parameterization and filtered indexes.');
5358
5359 IF EXISTS (SELECT 1/0
5360 FROM ##BlitzCacheProcs
5361 WHERE unparameterized_query = 1
5362 AND SPID = @@SPID)
5363 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5364 VALUES (@@SPID,
5365 23,
5366 100,
5367 'Parameterization',
5368 'Unparameterized queries',
5369 'http://brentozar.com/blitzcache/unparameterized-queries',
5370 'Unparameterized queries found. These could be ad hoc queries, data exploration, or queries using "OPTIMIZE FOR UNKNOWN".');
5371
5372 IF EXISTS (SELECT 1/0
5373 FROM ##BlitzCacheProcs
5374 WHERE is_trivial = 1
5375 AND SPID = @@SPID)
5376 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5377 VALUES (@@SPID,
5378 24,
5379 100,
5380 'Execution Plans',
5381 'Trivial Plans',
5382 'http://brentozar.com/blitzcache/trivial-plans',
5383 'Trivial plans get almost no optimization. If you''re finding these in the top worst queries, something may be going wrong.');
5384
5385 IF EXISTS (SELECT 1/0
5386 FROM ##BlitzCacheProcs p
5387 WHERE p.is_forced_serial= 1
5388 AND SPID = @@SPID)
5389 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5390 VALUES (@@SPID,
5391 25,
5392 10,
5393 'Execution Plans',
5394 'Forced Serialization',
5395 'http://www.brentozar.com/blitzcache/forced-serialization/',
5396 'Something in your plan is forcing a serial query. Further investigation is needed if this is not by design.') ;
5397
5398 IF EXISTS (SELECT 1/0
5399 FROM ##BlitzCacheProcs p
5400 WHERE p.is_key_lookup_expensive= 1
5401 AND SPID = @@SPID)
5402 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5403 VALUES (@@SPID,
5404 26,
5405 100,
5406 'Execution Plans',
5407 'Expensive Key Lookups',
5408 'http://www.brentozar.com/blitzcache/expensive-key-lookups/',
5409 'There''s a key lookup in your plan that costs >=50% of the total plan cost.') ;
5410
5411 IF EXISTS (SELECT 1/0
5412 FROM ##BlitzCacheProcs p
5413 WHERE p.is_remote_query_expensive= 1
5414 AND SPID = @@SPID)
5415 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5416 VALUES (@@SPID,
5417 28,
5418 100,
5419 'Execution Plans',
5420 'Expensive Remote Query',
5421 'http://www.brentozar.com/blitzcache/expensive-remote-query/',
5422 'There''s a remote query in your plan that costs >=50% of the total plan cost.') ;
5423
5424 IF EXISTS (SELECT 1/0
5425 FROM ##BlitzCacheProcs p
5426 WHERE p.trace_flags_session IS NOT NULL
5427 AND SPID = @@SPID)
5428 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5429 VALUES (@@SPID,
5430 29,
5431 200,
5432 'Trace Flags',
5433 'Session Level Trace Flags Enabled',
5434 'https://www.brentozar.com/blitz/trace-flags-enabled-globally/',
5435 'Someone is enabling session level Trace Flags in a query.') ;
5436
5437 IF EXISTS (SELECT 1/0
5438 FROM ##BlitzCacheProcs p
5439 WHERE p.is_unused_grant IS NOT NULL
5440 AND SPID = @@SPID)
5441 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5442 VALUES (@@SPID,
5443 30,
5444 100,
5445 'Unused memory grants',
5446 'Queries are asking for more memory than they''re using',
5447 'https://www.brentozar.com/blitzcache/unused-memory-grants/',
5448 'Queries have large unused memory grants. This can cause concurrency issues, if queries are waiting a long time to get memory to run.') ;
5449
5450 IF EXISTS (SELECT 1/0
5451 FROM ##BlitzCacheProcs p
5452 WHERE p.function_count > 0
5453 AND SPID = @@SPID)
5454 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5455 VALUES (@@SPID,
5456 31,
5457 100,
5458 'Compute Scalar That References A Function',
5459 'This could be trouble if you''re using Scalar Functions or MSTVFs',
5460 'https://www.brentozar.com/blitzcache/compute-scalar-functions/',
5461 'Both of these will force queries to run serially, run at least once per row, and may result in poor cardinality estimates.') ;
5462
5463 IF EXISTS (SELECT 1/0
5464 FROM ##BlitzCacheProcs p
5465 WHERE p.clr_function_count > 0
5466 AND SPID = @@SPID)
5467 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5468 VALUES (@@SPID,
5469 32,
5470 100,
5471 'Compute Scalar That References A CLR Function',
5472 'This could be trouble if your CLR functions perform data access',
5473 'https://www.brentozar.com/blitzcache/compute-scalar-functions/',
5474 'May force queries to run serially, run at least once per row, and may result in poor cardinality estimates.') ;
5475
5476
5477 IF EXISTS (SELECT 1/0
5478 FROM ##BlitzCacheProcs p
5479 WHERE p.is_table_variable = 1
5480 AND SPID = @@SPID)
5481 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5482 VALUES (@@SPID,
5483 33,
5484 100,
5485 'Table Variables detected',
5486 'Beware nasty side effects',
5487 'https://www.brentozar.com/blitzcache/table-variables/',
5488 'All modifications are single threaded, and selects have really low row estimates.') ;
5489
5490 IF EXISTS (SELECT 1/0
5491 FROM ##BlitzCacheProcs p
5492 WHERE p.no_stats_warning = 1
5493 AND SPID = @@SPID)
5494 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5495 VALUES (@@SPID,
5496 35,
5497 100,
5498 'Columns with no statistics',
5499 'Poor cardinality estimates may ensue',
5500 'https://www.brentozar.com/blitzcache/columns-no-statistics/',
5501 'Sometimes this happens with indexed views, other times because auto create stats is turned off.') ;
5502
5503 IF EXISTS (SELECT 1/0
5504 FROM ##BlitzCacheProcs p
5505 WHERE p.relop_warnings = 1
5506 AND SPID = @@SPID)
5507 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5508 VALUES (@@SPID,
5509 36,
5510 100,
5511 'Operator Warnings',
5512 'SQL is throwing operator level plan warnings',
5513 'http://brentozar.com/blitzcache/query-plan-warnings/',
5514 'Check the plan for more details.') ;
5515
5516 IF EXISTS (SELECT 1/0
5517 FROM ##BlitzCacheProcs p
5518 WHERE p.is_table_scan = 1
5519 AND SPID = @@SPID)
5520 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5521 VALUES (@@SPID,
5522 37,
5523 100,
5524 'Table Scans',
5525 'Your database has HEAPs',
5526 'https://www.brentozar.com/archive/2012/05/video-heaps/',
5527 'This may not be a problem. Run sp_BlitzIndex for more information.') ;
5528
5529 IF EXISTS (SELECT 1/0
5530 FROM ##BlitzCacheProcs p
5531 WHERE p.backwards_scan = 1
5532 AND SPID = @@SPID)
5533 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5534 VALUES (@@SPID,
5535 38,
5536 200,
5537 'Backwards Scans',
5538 'Indexes are being read backwards',
5539 'https://www.brentozar.com/blitzcache/backwards-scans/',
5540 'This isn''t always a problem. They can cause serial zones in plans, and may need an index to match sort order.') ;
5541
5542 IF EXISTS (SELECT 1/0
5543 FROM ##BlitzCacheProcs p
5544 WHERE p.forced_index = 1
5545 AND SPID = @@SPID)
5546 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5547 VALUES (@@SPID,
5548 39,
5549 100,
5550 'Index forcing',
5551 'Someone is using hints to force index usage',
5552 'https://www.brentozar.com/blitzcache/optimizer-forcing/',
5553 'This can cause inefficient plans, and will prevent missing index requests.') ;
5554
5555 IF EXISTS (SELECT 1/0
5556 FROM ##BlitzCacheProcs p
5557 WHERE p.forced_seek = 1
5558 OR p.forced_scan = 1
5559 AND SPID = @@SPID)
5560 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5561 VALUES (@@SPID,
5562 40,
5563 100,
5564 'Seek/Scan forcing',
5565 'Someone is using hints to force index seeks/scans',
5566 'https://www.brentozar.com/blitzcache/optimizer-forcing/',
5567 'This can cause inefficient plans by taking seek vs scan choice away from the optimizer.') ;
5568
5569 IF EXISTS (SELECT 1/0
5570 FROM ##BlitzCacheProcs p
5571 WHERE p.columnstore_row_mode = 1
5572 AND SPID = @@SPID)
5573 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5574 VALUES (@@SPID,
5575 41,
5576 100,
5577 'ColumnStore indexes operating in Row Mode',
5578 'Batch Mode is optimal for ColumnStore indexes',
5579 'https://www.brentozar.com/blitzcache/columnstore-indexes-operating-row-mode/',
5580 'ColumnStore indexes operating in Row Mode indicate really poor query choices.') ;
5581
5582 IF EXISTS (SELECT 1/0
5583 FROM ##BlitzCacheProcs p
5584 WHERE p.is_computed_scalar = 1
5585 AND SPID = @@SPID)
5586 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5587 VALUES (@@SPID,
5588 42,
5589 50,
5590 'Computed Columns Referencing Scalar UDFs',
5591 'This makes a whole lot of stuff run serially',
5592 'https://www.brentozar.com/blitzcache/computed-columns-referencing-functions/',
5593 'This can cause a whole mess of bad serializartion problems.') ;
5594
5595 IF EXISTS (SELECT 1/0
5596 FROM ##BlitzCacheProcs p
5597 WHERE p.is_sort_expensive = 1
5598 AND SPID = @@SPID)
5599 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5600 VALUES (@@SPID,
5601 43,
5602 100,
5603 'Execution Plans',
5604 'Expensive Sort',
5605 'http://www.brentozar.com/blitzcache/expensive-sorts/',
5606 'There''s a sort in your plan that costs >=50% of the total plan cost.') ;
5607
5608 IF EXISTS (SELECT 1/0
5609 FROM ##BlitzCacheProcs p
5610 WHERE p.is_computed_filter = 1
5611 AND SPID = @@SPID)
5612 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5613 VALUES (@@SPID,
5614 44,
5615 50,
5616 'Filters Referencing Scalar UDFs',
5617 'This forces serialization',
5618 'https://www.brentozar.com/blitzcache/compute-scalar-functions/',
5619 'Someone put a Scalar UDF in the WHERE clause!') ;
5620
5621 IF EXISTS (SELECT 1/0
5622 FROM ##BlitzCacheProcs p
5623 WHERE p.index_ops >= 5
5624 AND SPID = @@SPID)
5625 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5626 VALUES (@@SPID,
5627 45,
5628 100,
5629 'Many Indexes Modified',
5630 'Write Queries Are Hitting >= 5 Indexes',
5631 'https://www.brentozar.com/blitzcache/many-indexes-modified/',
5632 'This can cause lots of hidden I/O -- Run sp_BlitzIndex for more information.') ;
5633
5634 IF EXISTS (SELECT 1/0
5635 FROM ##BlitzCacheProcs p
5636 WHERE p.is_row_level = 1
5637 AND SPID = @@SPID)
5638 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5639 VALUES (@@SPID,
5640 46,
5641 200,
5642 'Plan Confusion',
5643 'Row Level Security is in use',
5644 'https://www.brentozar.com/blitzcache/row-level-security/',
5645 'You may see a lot of confusing junk in your query plan.') ;
5646
5647 IF EXISTS (SELECT 1/0
5648 FROM ##BlitzCacheProcs p
5649 WHERE p.is_spatial = 1
5650 AND SPID = @@SPID)
5651 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5652 VALUES (@@SPID,
5653 47,
5654 200,
5655 'Spatial Abuse',
5656 'You hit a Spatial Index',
5657 'https://www.brentozar.com/blitzcache/spatial-indexes/',
5658 'Purely informational.') ;
5659
5660 IF EXISTS (SELECT 1/0
5661 FROM ##BlitzCacheProcs p
5662 WHERE p.index_dml = 1
5663 AND SPID = @@SPID)
5664 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5665 VALUES (@@SPID,
5666 48,
5667 150,
5668 'Index DML',
5669 'Indexes were created or dropped',
5670 'https://www.brentozar.com/blitzcache/index-dml/',
5671 'This can cause recompiles and stuff.') ;
5672
5673 IF EXISTS (SELECT 1/0
5674 FROM ##BlitzCacheProcs p
5675 WHERE p.table_dml = 1
5676 AND SPID = @@SPID)
5677 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5678 VALUES (@@SPID,
5679 49,
5680 150,
5681 'Table DML',
5682 'Tables were created or dropped',
5683 'https://www.brentozar.com/blitzcache/table-dml/',
5684 'This can cause recompiles and stuff.') ;
5685
5686 IF EXISTS (SELECT 1/0
5687 FROM ##BlitzCacheProcs p
5688 WHERE p.long_running_low_cpu = 1
5689 AND SPID = @@SPID)
5690 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5691 VALUES (@@SPID,
5692 50,
5693 150,
5694 'Long Running Low CPU',
5695 'You have a query that runs for much longer than it uses CPU',
5696 'https://www.brentozar.com/blitzcache/long-running-low-cpu/',
5697 'This can be a sign of blocking, linked servers, or poor client application code (ASYNC_NETWORK_IO).') ;
5698
5699 IF EXISTS (SELECT 1/0
5700 FROM ##BlitzCacheProcs p
5701 WHERE p.low_cost_high_cpu = 1
5702 AND SPID = @@SPID)
5703 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5704 VALUES (@@SPID,
5705 51,
5706 150,
5707 'Low Cost Query With High CPU',
5708 'You have a low cost query that uses a lot of CPU',
5709 'https://www.brentozar.com/blitzcache/low-cost-high-cpu/',
5710 'This can be a sign of functions or Dynamic SQL that calls black-box code.') ;
5711
5712 IF EXISTS (SELECT 1/0
5713 FROM ##BlitzCacheProcs p
5714 WHERE p.stale_stats = 1
5715 AND SPID = @@SPID)
5716 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5717 VALUES (@@SPID,
5718 52,
5719 150,
5720 'Biblical Statistics',
5721 'Statistics used in queries are >7 days old with >100k modifications',
5722 'https://www.brentozar.com/blitzcache/stale-statistics/',
5723 'Ever heard of updating statistics?') ;
5724
5725 IF EXISTS (SELECT 1/0
5726 FROM ##BlitzCacheProcs p
5727 WHERE p.is_adaptive = 1
5728 AND SPID = @@SPID)
5729 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5730 VALUES (@@SPID,
5731 53,
5732 200,
5733 'Adaptive joins',
5734 'This is pretty cool -- you''re living in the future.',
5735 'https://www.brentozar.com/blitzcache/adaptive-joins/',
5736 'Joe Sack rules.') ;
5737
5738 IF EXISTS (SELECT 1/0
5739 FROM ##BlitzCacheProcs p
5740 WHERE p.is_spool_expensive = 1
5741 )
5742 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5743 VALUES (@@SPID,
5744 54,
5745 150,
5746 'Expensive Index Spool',
5747 'You have an index spool, this is usually a sign that there''s an index missing somewhere.',
5748 'https://www.brentozar.com/blitzcache/eager-index-spools/',
5749 'Check operator predicates and output for index definition guidance') ;
5750
5751 IF EXISTS (SELECT 1/0
5752 FROM ##BlitzCacheProcs p
5753 WHERE p.is_spool_more_rows = 1
5754 )
5755 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5756 VALUES (@@SPID,
5757 55,
5758 150,
5759 'Index Spools Many Rows',
5760 'You have an index spool that spools more rows than the query returns',
5761 'https://www.brentozar.com/blitzcache/eager-index-spools/',
5762 'Check operator predicates and output for index definition guidance') ;
5763
5764 IF EXISTS (SELECT 1/0
5765 FROM ##BlitzCacheProcs p
5766 WHERE p.is_bad_estimate = 1
5767 )
5768 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5769 VALUES (@@SPID,
5770 56,
5771 100,
5772 'Potentially bad cardinality estimates',
5773 'Estimated rows are different from average rows by a factor of 10000',
5774 'https://www.brentozar.com/blitzcache/bad-estimates/',
5775 'This may indicate a performance problem if mismatches occur regularly') ;
5776
5777 IF EXISTS (SELECT 1/0
5778 FROM ##BlitzCacheProcs p
5779 WHERE p.is_paul_white_electric = 1
5780 )
5781 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5782 VALUES (@@SPID,
5783 57,
5784 200,
5785 'Is Paul White Electric?',
5786 'This query has a Switch operator in it!',
5787 'http://sqlblog.com/blogs/paul_white/archive/2013/06/11/hello-operator-my-switch-is-bored.aspx',
5788 'You should email this query plan to Paul: SQLkiwi at gmail dot com') ;
5789
5790 IF @v >= 14 OR (@v = 13 AND @build >= 5026)
5791 BEGIN
5792
5793 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5794 SELECT
5795 @@SPID,
5796 999,
5797 200,
5798 'Database Level Statistics',
5799 'The database ' + sa.[Database] + ' last had a stats update on ' + CONVERT(NVARCHAR(10), CONVERT(DATE, MAX(sa.LastUpdate))) + ' and has ' + CONVERT(NVARCHAR(10), AVG(sa.ModificationCount)) + ' modifications on average.' AS [Finding],
5800 'https://www.brentozar.com/blitzcache/stale-statistics/' AS URL,
5801 'Consider updating statistics more frequently,' AS [Details]
5802 FROM #stats_agg AS sa
5803 GROUP BY sa.[Database]
5804 HAVING MAX(sa.LastUpdate) <= DATEADD(DAY, -7, SYSDATETIME())
5805 AND AVG(sa.ModificationCount) >= 100000;
5806
5807 IF EXISTS (SELECT 1/0
5808 FROM ##BlitzCacheProcs p
5809 WHERE p.is_row_goal = 1
5810 )
5811 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5812 VALUES (@@SPID,
5813 58,
5814 200,
5815 'Row Goals',
5816 'This query had row goals introduced',
5817 'https://www.brentozar.com/go/rowgoals/',
5818 'This can be good or bad, and should be investigated for high read queries') ;
5819
5820 IF EXISTS (SELECT 1/0
5821 FROM ##BlitzCacheProcs p
5822 WHERE p.is_big_spills = 1
5823 )
5824 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5825 VALUES (@@SPID,
5826 59,
5827 100,
5828 'tempdb Spills',
5829 'This query spills >500mb to tempdb on average',
5830 'https://www.brentozar.com/blitzcache/tempdb-spills/',
5831 'One way or another, this query didn''t get enough memory') ;
5832
5833
5834 END;
5835
5836 IF EXISTS (SELECT 1/0
5837 FROM ##BlitzCacheProcs p
5838 WHERE p.is_mstvf = 1
5839 )
5840 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5841 VALUES (@@SPID,
5842 60,
5843 100,
5844 'MSTVFs',
5845 'These have many of the same problems scalar UDFs have',
5846 'http://brentozar.com/blitzcache/tvf-join/',
5847 'Execution plans have been found that join to table valued functions (TVFs). TVFs produce inaccurate estimates of the number of rows returned and can lead to any number of query plan problems.');
5848
5849 IF EXISTS (SELECT 1/0
5850 FROM ##BlitzCacheProcs p
5851 WHERE p.is_mm_join = 1
5852 )
5853 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5854 VALUES (@@SPID,
5855 61,
5856 100,
5857 'Many to Many Merge',
5858 'These use secret worktables that could be doing lots of reads',
5859 'https://www.brentozar.com/archive/2018/04/many-mysteries-merge-joins/',
5860 'Occurs when join inputs aren''t known to be unique. Can be really bad when parallel.');
5861
5862 IF EXISTS (SELECT 1/0
5863 FROM ##BlitzCacheProcs p
5864 WHERE p.is_nonsargable = 1
5865 )
5866 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5867 VALUES (@@SPID,
5868 62,
5869 50,
5870 'Non-SARGable queries',
5871 'Queries may have non-SARGable predicates',
5872 'https://www.brentozar.com/blitzcache/non-sargable-predicates/',
5873 'Looks for intrinsic functions and expressions as predicates, and leading wildcard LIKE searches.');
5874
5875 IF EXISTS (SELECT 1/0
5876 FROM ##BlitzCacheProcs p
5877 WHERE CompileTime > 5000
5878 )
5879 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5880 VALUES (@@SPID,
5881 63,
5882 100,
5883 'High Compile Time',
5884 'Queries taking >5 seconds to compile',
5885 'https://www.brentozar.com/blitzcache/high-compilers/',
5886 'This can be normal for large plans, but be careful if they compile frequently');
5887
5888 IF EXISTS (SELECT 1/0
5889 FROM ##BlitzCacheProcs p
5890 WHERE CompileCPU > 5000
5891 )
5892 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5893 VALUES (@@SPID,
5894 64,
5895 50,
5896 'High Compile CPU',
5897 'Queries taking >5 seconds of CPU to compile',
5898 'https://www.brentozar.com/blitzcache/high-compilers/',
5899 'If CPU is high and plans like this compile frequently, they may be related');
5900
5901 IF EXISTS (SELECT 1/0
5902 FROM ##BlitzCacheProcs p
5903 WHERE CompileMemory > 1024
5904 AND ((CompileMemory) / (1 * CASE WHEN MaxCompileMemory = 0 THEN 1 ELSE MaxCompileMemory END) * 100.) >= 10.
5905 )
5906 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5907 VALUES (@@SPID,
5908 65,
5909 50,
5910 'High Compile Memory',
5911 'Queries taking 10% of Max Compile Memory',
5912 'https://www.brentozar.com/blitzcache/high-compilers/',
5913 'If you see high RESOURCE_SEMAPHORE_QUERY_COMPILE waits, these may be related');
5914
5915 IF EXISTS (SELECT 1/0
5916 FROM #plan_creation p
5917 WHERE (p.percent_24 > 0)
5918 AND SPID = @@SPID)
5919 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5920 SELECT SPID,
5921 999,
5922 254,
5923 'Plan Cache Information',
5924 'You have ' + CONVERT(NVARCHAR(10), ISNULL(p.total_plans, 0))
5925 + ' total plans in your cache, with '
5926 + CONVERT(NVARCHAR(10), ISNULL(p.percent_24, 0))
5927 + '% plans created in the past 24 hours, '
5928 + CONVERT(NVARCHAR(10), ISNULL(p.percent_4, 0))
5929 + '% created in the past 4 hours, and '
5930 + CONVERT(NVARCHAR(10), ISNULL(p.percent_1, 0))
5931 + '% created in the past 1 hour.',
5932 '',
5933 'If these percentages are high, it may be a sign of memory pressure or plan cache instability.'
5934 FROM #plan_creation p ;
5935
5936 IF @v >= 11
5937 BEGIN
5938 IF EXISTS (SELECT 1/0
5939 FROM #trace_flags AS tf
5940 WHERE tf.global_trace_flags IS NOT NULL
5941 )
5942 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5943 VALUES (@@SPID,
5944 1000,
5945 255,
5946 'Global Trace Flags Enabled',
5947 'You have Global Trace Flags enabled on your server',
5948 'https://www.brentozar.com/blitz/trace-flags-enabled-globally/',
5949 'You have the following Global Trace Flags enabled: ' + (SELECT TOP 1 tf.global_trace_flags FROM #trace_flags AS tf WHERE tf.global_trace_flags IS NOT NULL)) ;
5950 END;
5951
5952 IF NOT EXISTS (SELECT 1/0
5953 FROM ##BlitzCacheResults AS bcr
5954 WHERE bcr.Priority = 2147483646
5955 )
5956 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5957 VALUES (@@SPID,
5958 2147483646,
5959 255,
5960 'Need more help?' ,
5961 'Paste your plan on the internet!',
5962 'http://pastetheplan.com',
5963 'This makes it easy to share plans and post them to Q&A sites like https://dba.stackexchange.com/!') ;
5964
5965
5966
5967 IF NOT EXISTS (SELECT 1/0
5968 FROM ##BlitzCacheResults AS bcr
5969 WHERE bcr.Priority = 2147483647
5970 )
5971 INSERT INTO ##BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
5972 VALUES (@@SPID,
5973 2147483647,
5974 255,
5975 'Thanks for using sp_BlitzCache!' ,
5976 'From Your Community Volunteers',
5977 'http://FirstResponderKit.org',
5978 'We hope you found this tool useful. Current version: ' + @Version + ' released on ' + CONVERT(NVARCHAR(30), @VersionDate) + '.') ;
5979
5980 END;
5981
5982
5983 SELECT Priority,
5984 FindingsGroup,
5985 Finding,
5986 URL,
5987 Details,
5988 CheckID
5989 FROM ##BlitzCacheResults
5990 WHERE SPID = @@SPID
5991 GROUP BY Priority,
5992 FindingsGroup,
5993 Finding,
5994 URL,
5995 Details,
5996 CheckID
5997 ORDER BY Priority ASC, FindingsGroup, Finding, CheckID ASC
5998 OPTION (RECOMPILE);
5999END;
6000
6001IF @Debug = 1
6002 BEGIN
6003
6004 SELECT '##BlitzCacheResults' AS table_name, *
6005 FROM ##BlitzCacheResults
6006 OPTION ( RECOMPILE );
6007
6008 SELECT '##BlitzCacheProcs' AS table_name, *
6009 FROM ##BlitzCacheProcs
6010 OPTION ( RECOMPILE );
6011
6012 SELECT '#statements' AS table_name, *
6013 FROM #statements AS s
6014 OPTION (RECOMPILE);
6015
6016 SELECT '#query_plan' AS table_name, *
6017 FROM #query_plan AS qp
6018 OPTION (RECOMPILE);
6019
6020 SELECT '#relop' AS table_name, *
6021 FROM #relop AS r
6022 OPTION (RECOMPILE);
6023
6024 SELECT '#only_query_hashes' AS table_name, *
6025 FROM #only_query_hashes
6026 OPTION ( RECOMPILE );
6027
6028 SELECT '#ignore_query_hashes' AS table_name, *
6029 FROM #ignore_query_hashes
6030 OPTION ( RECOMPILE );
6031
6032 SELECT '#only_sql_handles' AS table_name, *
6033 FROM #only_sql_handles
6034 OPTION ( RECOMPILE );
6035
6036 SELECT '#ignore_sql_handles' AS table_name, *
6037 FROM #ignore_sql_handles
6038 OPTION ( RECOMPILE );
6039
6040 SELECT '#p' AS table_name, *
6041 FROM #p
6042 OPTION ( RECOMPILE );
6043
6044 SELECT '#checkversion' AS table_name, *
6045 FROM #checkversion
6046 OPTION ( RECOMPILE );
6047
6048 SELECT '#configuration' AS table_name, *
6049 FROM #configuration
6050 OPTION ( RECOMPILE );
6051
6052 SELECT '#stored_proc_info' AS table_name, *
6053 FROM #stored_proc_info
6054 OPTION ( RECOMPILE );
6055
6056 SELECT '#conversion_info' AS table_name, *
6057 FROM #conversion_info AS ci
6058 OPTION ( RECOMPILE );
6059
6060 SELECT '#variable_info' AS table_name, *
6061 FROM #variable_info AS vi
6062 OPTION ( RECOMPILE );
6063
6064 SELECT '#missing_index_xml' AS table_name, *
6065 FROM #missing_index_xml AS mix
6066 OPTION ( RECOMPILE );
6067
6068 SELECT '#missing_index_schema' AS table_name, *
6069 FROM #missing_index_schema AS mis
6070 OPTION ( RECOMPILE );
6071
6072 SELECT '#missing_index_usage' AS table_name, *
6073 FROM #missing_index_usage AS miu
6074 OPTION ( RECOMPILE );
6075
6076 SELECT '#missing_index_detail' AS table_name, *
6077 FROM #missing_index_detail AS mid
6078 OPTION ( RECOMPILE );
6079
6080 SELECT '#missing_index_pretty' AS table_name, *
6081 FROM #missing_index_pretty AS mip
6082 OPTION ( RECOMPILE );
6083
6084 SELECT '#plan_creation' AS table_name, *
6085 FROM #plan_creation
6086 OPTION ( RECOMPILE );
6087
6088 SELECT '#plan_cost' AS table_name, *
6089 FROM #plan_cost
6090 OPTION ( RECOMPILE );
6091
6092 SELECT '#proc_costs' AS table_name, *
6093 FROM #proc_costs
6094 OPTION ( RECOMPILE );
6095
6096 SELECT '#stats_agg' AS table_name, *
6097 FROM #stats_agg
6098 OPTION ( RECOMPILE );
6099
6100 SELECT '#trace_flags' AS table_name, *
6101 FROM #trace_flags
6102 OPTION ( RECOMPILE );
6103
6104 END;
6105
6106
6107RETURN; --Avoid going into the AllSort GOTO
6108
6109/*Begin code to sort by all*/
6110AllSorts:
6111RAISERROR('Beginning all sort loop', 0, 1) WITH NOWAIT;
6112
6113
6114IF (
6115 @Top > 10
6116 AND @BringThePain = 0
6117 )
6118 BEGIN
6119 RAISERROR(
6120 '
6121 You''ve chosen a value greater than 10 to sort the whole plan cache by.
6122 That can take a long time and harm performance.
6123 Please choose a number <= 10, or set @BringThePain = 1 to signify you understand this might be a bad idea.
6124 ', 0, 1) WITH NOWAIT;
6125 RETURN;
6126 END;
6127
6128
6129IF OBJECT_ID('tempdb..#checkversion_allsort') IS NULL
6130 BEGIN
6131 CREATE TABLE #checkversion_allsort
6132 (
6133 version NVARCHAR(128),
6134 common_version AS SUBSTRING(version, 1, CHARINDEX('.', version) + 1),
6135 major AS PARSENAME(CONVERT(VARCHAR(32), version), 4),
6136 minor AS PARSENAME(CONVERT(VARCHAR(32), version), 3),
6137 build AS PARSENAME(CONVERT(VARCHAR(32), version), 2),
6138 revision AS PARSENAME(CONVERT(VARCHAR(32), version), 1)
6139 );
6140
6141 INSERT INTO #checkversion_allsort
6142 (version)
6143 SELECT CAST(SERVERPROPERTY('ProductVersion') AS NVARCHAR(128))
6144 OPTION ( RECOMPILE );
6145 END;
6146
6147
6148SELECT @v = common_version,
6149 @build = build
6150FROM #checkversion_allsort
6151OPTION ( RECOMPILE );
6152
6153IF OBJECT_ID('tempdb.. #bou_allsort') IS NULL
6154 BEGIN
6155 CREATE TABLE #bou_allsort
6156 (
6157 Id INT IDENTITY(1, 1),
6158 DatabaseName NVARCHAR(128),
6159 Cost FLOAT,
6160 QueryText NVARCHAR(MAX),
6161 QueryType NVARCHAR(258),
6162 Warnings VARCHAR(MAX),
6163 QueryPlan XML,
6164 missing_indexes XML,
6165 implicit_conversion_info XML,
6166 cached_execution_parameters XML,
6167 ExecutionCount NVARCHAR(30),
6168 ExecutionsPerMinute MONEY,
6169 ExecutionWeight MONEY,
6170 TotalCPU NVARCHAR(30),
6171 AverageCPU NVARCHAR(30),
6172 CPUWeight MONEY,
6173 TotalDuration NVARCHAR(30),
6174 AverageDuration NVARCHAR(30),
6175 DurationWeight MONEY,
6176 TotalReads NVARCHAR(30),
6177 AverageReads NVARCHAR(30),
6178 ReadWeight MONEY,
6179 TotalWrites NVARCHAR(30),
6180 AverageWrites NVARCHAR(30),
6181 WriteWeight MONEY,
6182 AverageReturnedRows MONEY,
6183 MinGrantKB NVARCHAR(30),
6184 MaxGrantKB NVARCHAR(30),
6185 MinUsedGrantKB NVARCHAR(30),
6186 MaxUsedGrantKB NVARCHAR(30),
6187 AvgMaxMemoryGrant MONEY,
6188 MinSpills NVARCHAR(30),
6189 MaxSpills NVARCHAR(30),
6190 TotalSpills NVARCHAR(30),
6191 AvgSpills MONEY,
6192 PlanCreationTime DATETIME,
6193 LastExecutionTime DATETIME,
6194 PlanHandle VARBINARY(64),
6195 SqlHandle VARBINARY(64),
6196 SetOptions VARCHAR(MAX),
6197 Pattern NVARCHAR(20)
6198 );
6199 END;
6200
6201DECLARE @AllSortSql NVARCHAR(MAX) = N'';
6202DECLARE @MemGrant BIT;
6203SELECT @MemGrant = CASE WHEN (
6204 ( @v < 11 )
6205 OR (
6206 @v = 11
6207 AND @build < 6020
6208 )
6209 OR (
6210 @v = 12
6211 AND @build < 5000
6212 )
6213 OR (
6214 @v = 13
6215 AND @build < 1601
6216 )
6217 ) THEN 0
6218 ELSE 1
6219 END;
6220
6221DECLARE @Spills BIT;
6222SELECT @Spills = CASE WHEN (@v >= 15 OR (@v = 14 AND @build >= 3015) OR (@v = 13 AND @build >= 5026)) THEN 1 ELSE 0 END;
6223
6224
6225IF LOWER(@SortOrder) = 'all'
6226BEGIN
6227RAISERROR('Beginning for ALL', 0, 1) WITH NOWAIT;
6228SET @AllSortSql += N'
6229 DECLARE @ISH NVARCHAR(MAX) = N''''
6230
6231 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6232 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6233 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6234 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6235
6236 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''cpu'', @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6237
6238 UPDATE #bou_allsort SET Pattern = ''cpu'' WHERE Pattern IS NULL OPTION(RECOMPILE);
6239
6240 SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6241
6242 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6243 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6244 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6245 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6246
6247 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''reads'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6248
6249 UPDATE #bou_allsort SET Pattern = ''reads'' WHERE Pattern IS NULL OPTION(RECOMPILE);
6250
6251 SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6252
6253 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6254 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6255 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6256 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6257
6258 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''writes'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6259
6260 UPDATE #bou_allsort SET Pattern = ''writes'' WHERE Pattern IS NULL OPTION(RECOMPILE);
6261
6262 SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6263
6264 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6265 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6266 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6267 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6268
6269 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''duration'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6270
6271 UPDATE #bou_allsort SET Pattern = ''duration'' WHERE Pattern IS NULL OPTION(RECOMPILE);
6272
6273 SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6274
6275 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6276 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6277 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6278 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6279
6280 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''executions'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6281
6282 UPDATE #bou_allsort SET Pattern = ''executions'' WHERE Pattern IS NULL OPTION(RECOMPILE);
6283
6284 ';
6285
6286 IF @MemGrant = 0
6287 BEGIN
6288 IF @ExportToExcel = 1
6289 BEGIN
6290 SET @AllSortSql += N' UPDATE #bou_allsort
6291 SET
6292 QueryPlan = NULL,
6293 implicit_conversion_info = NULL,
6294 cached_execution_parameters = NULL,
6295 missing_indexes = NULL
6296 OPTION (RECOMPILE);
6297
6298 UPDATE ##BlitzCacheProcs
6299 SET QueryText = SUBSTRING(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(QueryText)),'' '',''<>''),''><'',''''),''<>'','' ''), 1, 32000)
6300 OPTION(RECOMPILE);';
6301 END;
6302
6303 END;
6304
6305 IF @MemGrant = 1
6306 BEGIN
6307 SET @AllSortSql += N' SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6308
6309 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6310 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6311 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6312 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6313
6314 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''memory grant'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6315
6316 UPDATE #bou_allsort SET Pattern = ''memory grant'' WHERE Pattern IS NULL OPTION(RECOMPILE);';
6317 IF @ExportToExcel = 1
6318 BEGIN
6319 SET @AllSortSql += N' UPDATE #bou_allsort
6320 SET
6321 QueryPlan = NULL,
6322 implicit_conversion_info = NULL,
6323 cached_execution_parameters = NULL,
6324 missing_indexes = NULL
6325 OPTION (RECOMPILE);
6326
6327 UPDATE ##BlitzCacheProcs
6328 SET QueryText = SUBSTRING(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(QueryText)),'' '',''<>''),''><'',''''),''<>'','' ''), 1, 32000)
6329 OPTION(RECOMPILE);';
6330 END;
6331
6332 END;
6333
6334 IF @Spills = 0
6335 BEGIN
6336 IF @ExportToExcel = 1
6337 BEGIN
6338 SET @AllSortSql += N' UPDATE #bou_allsort
6339 SET
6340 QueryPlan = NULL,
6341 implicit_conversion_info = NULL,
6342 cached_execution_parameters = NULL,
6343 missing_indexes = NULL
6344 OPTION (RECOMPILE);
6345
6346 UPDATE ##BlitzCacheProcs
6347 SET QueryText = SUBSTRING(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(QueryText)),'' '',''<>''),''><'',''''),''<>'','' ''), 1, 32000)
6348 OPTION(RECOMPILE);';
6349 END;
6350
6351 END;
6352
6353 IF @Spills = 1
6354 BEGIN
6355 SET @AllSortSql += N' SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6356
6357 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6358 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6359 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6360 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6361
6362 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''spills'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6363
6364 UPDATE #bou_allsort SET Pattern = ''memory grant'' WHERE Pattern IS NULL OPTION(RECOMPILE);';
6365 IF @ExportToExcel = 1
6366 BEGIN
6367 SET @AllSortSql += N' UPDATE #bou_allsort
6368 SET
6369 QueryPlan = NULL,
6370 implicit_conversion_info = NULL,
6371 cached_execution_parameters = NULL,
6372 missing_indexes = NULL
6373 OPTION (RECOMPILE);
6374
6375 UPDATE ##BlitzCacheProcs
6376 SET QueryText = SUBSTRING(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(QueryText)),'' '',''<>''),''><'',''''),''<>'','' ''), 1, 32000)
6377 OPTION(RECOMPILE);';
6378 END;
6379
6380 END;
6381 SET @AllSortSql += N' SELECT DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters,ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6382 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6383 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6384 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions, Pattern
6385 FROM #bou_allsort
6386 ORDER BY Id
6387 OPTION(RECOMPILE); ';
6388
6389
6390END;
6391
6392
6393IF LOWER(@SortOrder) = 'all avg'
6394BEGIN
6395RAISERROR('Beginning for ALL AVG', 0, 1) WITH NOWAIT;
6396SET @AllSortSql += N'
6397 DECLARE @ISH NVARCHAR(MAX) = N''''
6398
6399 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6400 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6401 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6402 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6403
6404 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''avg cpu'', @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6405
6406 UPDATE #bou_allsort SET Pattern = ''avg cpu'' WHERE Pattern IS NULL OPTION(RECOMPILE);
6407
6408 SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6409
6410 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6411 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6412 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6413 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6414
6415 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''avg reads'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6416
6417 UPDATE #bou_allsort SET Pattern = ''avg reads'' WHERE Pattern IS NULL OPTION(RECOMPILE);
6418
6419 SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6420
6421 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6422 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6423 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6424 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6425
6426 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''avg writes'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6427
6428 UPDATE #bou_allsort SET Pattern = ''avg writes'' WHERE Pattern IS NULL OPTION(RECOMPILE);
6429
6430 SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6431
6432 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6433 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6434 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6435 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6436
6437 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''avg duration'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6438
6439 UPDATE #bou_allsort SET Pattern = ''avg duration'' WHERE Pattern IS NULL OPTION(RECOMPILE);
6440
6441 SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6442
6443 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6444 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6445 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6446 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6447
6448 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''avg executions'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6449
6450 UPDATE #bou_allsort SET Pattern = ''avg executions'' WHERE Pattern IS NULL OPTION(RECOMPILE);
6451
6452 ';
6453
6454 IF @MemGrant = 0
6455 BEGIN
6456 IF @ExportToExcel = 1
6457 BEGIN
6458 SET @AllSortSql += N' UPDATE #bou_allsort
6459 SET
6460 QueryPlan = NULL,
6461 implicit_conversion_info = NULL,
6462 cached_execution_parameters = NULL,
6463 missing_indexes = NULL
6464 OPTION (RECOMPILE);
6465
6466 UPDATE ##BlitzCacheProcs
6467 SET QueryText = SUBSTRING(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(QueryText)),'' '',''<>''),''><'',''''),''<>'','' ''), 1, 32000)
6468 OPTION(RECOMPILE);';
6469 END;
6470
6471 END;
6472
6473 IF @MemGrant = 1
6474 BEGIN
6475 SET @AllSortSql += N' SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6476
6477 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6478 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6479 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6480 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6481
6482 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''avg memory grant'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6483
6484 UPDATE #bou_allsort SET Pattern = ''avg memory grant'' WHERE Pattern IS NULL OPTION(RECOMPILE);';
6485 IF @ExportToExcel = 1
6486 BEGIN
6487 SET @AllSortSql += N' UPDATE #bou_allsort
6488 SET
6489 QueryPlan = NULL,
6490 implicit_conversion_info = NULL,
6491 cached_execution_parameters = NULL,
6492 missing_indexes = NULL
6493 OPTION (RECOMPILE);
6494
6495 UPDATE ##BlitzCacheProcs
6496 SET QueryText = SUBSTRING(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(QueryText)),'' '',''<>''),''><'',''''),''<>'','' ''), 1, 32000)
6497 OPTION(RECOMPILE);';
6498 END;
6499
6500 END;
6501
6502 IF @Spills = 0
6503 BEGIN
6504 IF @ExportToExcel = 1
6505 BEGIN
6506 SET @AllSortSql += N' UPDATE #bou_allsort
6507 SET
6508 QueryPlan = NULL,
6509 implicit_conversion_info = NULL,
6510 cached_execution_parameters = NULL,
6511 missing_indexes = NULL
6512 OPTION (RECOMPILE);
6513
6514 UPDATE ##BlitzCacheProcs
6515 SET QueryText = SUBSTRING(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(QueryText)),'' '',''<>''),''><'',''''),''<>'','' ''), 1, 32000)
6516 OPTION(RECOMPILE);';
6517 END;
6518
6519 END;
6520
6521 IF @Spills = 1
6522 BEGIN
6523 SET @AllSortSql += N' SELECT TOP 1 @ISH = STUFF((SELECT DISTINCT N'','' + CONVERT(NVARCHAR(MAX),b2.SqlHandle, 1) FROM #bou_allsort AS b2 FOR XML PATH(N''''), TYPE).value(N''.[1]'', N''NVARCHAR(MAX)''), 1, 1, N'''') OPTION(RECOMPILE);
6524
6525 INSERT #bou_allsort ( DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters, ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6526 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6527 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6528 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions )
6529
6530 EXEC sp_BlitzCache @ExpertMode = 0, @HideSummary = 1, @Top = @i_Top, @SortOrder = ''avg spills'', @IgnoreSqlHandles = @ISH, @DatabaseName = @i_DatabaseName WITH RECOMPILE;
6531
6532 UPDATE #bou_allsort SET Pattern = ''avg memory grant'' WHERE Pattern IS NULL OPTION(RECOMPILE);';
6533 IF @ExportToExcel = 1
6534 BEGIN
6535 SET @AllSortSql += N' UPDATE #bou_allsort
6536 SET
6537 QueryPlan = NULL,
6538 implicit_conversion_info = NULL,
6539 cached_execution_parameters = NULL,
6540 missing_indexes = NULL
6541 OPTION (RECOMPILE);
6542
6543 UPDATE ##BlitzCacheProcs
6544 SET QueryText = SUBSTRING(REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(QueryText)),'' '',''<>''),''><'',''''),''<>'','' ''), 1, 32000)
6545 OPTION(RECOMPILE);';
6546 END;
6547
6548 END;
6549
6550 SET @AllSortSql += N' SELECT DatabaseName, Cost, QueryText, QueryType, Warnings, QueryPlan, missing_indexes, implicit_conversion_info, cached_execution_parameters,ExecutionCount, ExecutionsPerMinute, ExecutionWeight,
6551 TotalCPU, AverageCPU, CPUWeight, TotalDuration, AverageDuration, DurationWeight, TotalReads, AverageReads,
6552 ReadWeight, TotalWrites, AverageWrites, WriteWeight, AverageReturnedRows, MinGrantKB, MaxGrantKB, MinUsedGrantKB,
6553 MaxUsedGrantKB, AvgMaxMemoryGrant, MinSpills, MaxSpills, TotalSpills, AvgSpills, PlanCreationTime, LastExecutionTime, PlanHandle, SqlHandle, SetOptions, Pattern
6554 FROM #bou_allsort
6555 ORDER BY Id
6556 OPTION(RECOMPILE); ';
6557END;
6558
6559 IF @Debug = 1
6560 BEGIN
6561 PRINT SUBSTRING(@AllSortSql, 0, 4000);
6562 PRINT SUBSTRING(@AllSortSql, 4000, 8000);
6563 PRINT SUBSTRING(@AllSortSql, 8000, 12000);
6564 PRINT SUBSTRING(@AllSortSql, 12000, 16000);
6565 PRINT SUBSTRING(@AllSortSql, 16000, 20000);
6566 PRINT SUBSTRING(@AllSortSql, 20000, 24000);
6567 PRINT SUBSTRING(@AllSortSql, 24000, 28000);
6568 PRINT SUBSTRING(@AllSortSql, 28000, 32000);
6569 PRINT SUBSTRING(@AllSortSql, 32000, 36000);
6570 PRINT SUBSTRING(@AllSortSql, 36000, 40000);
6571 END;
6572
6573 EXEC sys.sp_executesql @stmt = @AllSortSql, @params = N'@i_DatabaseName NVARCHAR(128), @i_Top INT', @i_DatabaseName = @DatabaseName, @i_Top = @Top;
6574
6575
6576/*End of AllSort section*/
6577
6578END; /*Final End*/
6579
6580GO