Skip to content

Commit 3231012

Browse files
authored
Merge pull request #586 from BrentOzarULTD/dev
2016-11-15 Release
2 parents 038056c + e16c50f commit 3231012

File tree

4 files changed

+216
-35
lines changed

4 files changed

+216
-35
lines changed

Documentation/sp_Blitz Checks by Priority.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ If you want to change anything about a check - the priority, finding, URL, or ID
211211
| 200 | Non-Default Server Config | Web Assistant Procedures | http://BrentOzar.com/go/conf | 1064 |
212212
| 200 | Non-Default Server Config | xp_cmdshell | http://BrentOzar.com/go/conf | 1065 |
213213
| 200 | Performance | Buffer Pool Extensions Enabled | http://BrentOzar.com/go/bpe | 174 |
214+
| 200 | Performance | Default Parallelism Settings | http://BrentOzar.com/go/cxpacket | 188 |
214215
| 200 | Performance | In-Memory OLTP (Hekaton) In Use | http://BrentOzar.com/go/hekaton | 146 |
215216
| 200 | Performance | Old Compatibility Level | http://BrentOzar.com/go/compatlevel | 62 |
216217
| 200 | Performance | Snapshot Backups Occurring | http://BrentOzar.com/go/snaps | 178 |

sp_Blitz.sql

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ ALTER PROCEDURE [dbo].[sp_Blitz]
2929
AS
3030
SET NOCOUNT ON;
3131
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
32-
SET @VersionDate = '20161022';
32+
SET @VersionDate = '20161115';
3333
SET @OutputType = UPPER(@OutputType);
3434

3535
IF @Help = 1 PRINT '
@@ -136,7 +136,9 @@ AS
136136
,@MsSinceWaitsCleared DECIMAL(38,0)
137137
,@CpuMsSinceWaitsCleared DECIMAL(38,0)
138138
,@ResultText NVARCHAR(MAX)
139-
,@crlf NVARCHAR(2);
139+
,@crlf NVARCHAR(2)
140+
,@Processors int
141+
,@NUMANodes int;
140142

141143

142144
SET @crlf = NCHAR(13) + NCHAR(10);
@@ -1499,6 +1501,40 @@ AS
14991501
WHERE cdUsed.name IS NULL;
15001502
END
15011503

1504+
IF NOT EXISTS ( SELECT 1
1505+
FROM #SkipChecks
1506+
WHERE DatabaseName IS NULL AND CheckID = 188 )
1507+
BEGIN
1508+
1509+
/* Let's set variables so that our query is still SARGable */
1510+
SET @Processors = (SELECT cpu_count FROM sys.dm_os_sys_info)
1511+
SET @NUMANodes = (SELECT COUNT(1)
1512+
FROM sys.dm_os_performance_counters pc
1513+
WHERE pc.object_name LIKE '%Buffer Node%'
1514+
AND counter_name = 'Page life expectancy')
1515+
/* If Cost Threshold for Parallelism is default then flag as a potential issue */
1516+
/* If MAXDOP is default and processors > 8 or NUMA nodes > 1 then flag as potential issue */
1517+
INSERT INTO #BlitzResults
1518+
( CheckID ,
1519+
Priority ,
1520+
FindingsGroup ,
1521+
Finding ,
1522+
URL ,
1523+
Details
1524+
)
1525+
SELECT 188 AS CheckID ,
1526+
200 AS Priority ,
1527+
'Performance' AS FindingsGroup ,
1528+
cr.name AS Finding ,
1529+
'http://BrentOzar.com/go/cxpacket' AS URL ,
1530+
( 'Set to ' + CAST(cr.value_in_use AS NVARCHAR(50)) + ', its default value. Changing this sp_configure setting may reduce CXPACKET waits.')
1531+
FROM sys.configurations cr
1532+
INNER JOIN #ConfigurationDefaults cd ON cd.name = cr.name
1533+
AND cr.value_in_use = cd.DefaultValue
1534+
WHERE cr.name = 'cost threshold for parallelism'
1535+
OR (cr.name = 'max degree of parallelism' AND (@NUMANodes > 1 OR @Processors > 8));
1536+
END
1537+
15021538

15031539
IF NOT EXISTS ( SELECT 1
15041540
FROM #SkipChecks

sp_BlitzCache.sql

Lines changed: 157 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ CREATE TABLE ##bou_BlitzCacheProcs (
145145
function_count INT,
146146
clr_function_count INT,
147147
is_table_variable BIT,
148+
no_stats_warning BIT,
149+
relop_warnings BIT,
150+
is_table_scan BIT,
151+
backwards_scan BIT,
152+
forced_index BIT,
153+
forced_seek BIT,
154+
forced_scan BIT,
148155
SetOptions VARCHAR(MAX),
149156
Warnings VARCHAR(MAX)
150157
);
@@ -732,12 +739,20 @@ BEGIN
732739
function_count INT,
733740
clr_function_count INT,
734741
is_table_variable BIT,
742+
no_stats_warning BIT,
743+
relop_warnings BIT,
744+
is_table_scan BIT,
745+
backwards_scan BIT,
746+
forced_index BIT,
747+
forced_seek BIT,
748+
forced_scan BIT,
735749
SetOptions VARCHAR(MAX),
736750
Warnings VARCHAR(MAX)
737751
);
738752
END
739753

740754
DECLARE @DurationFilter_i INT,
755+
@MinMemoryPerQuery INT,
741756
@msg NVARCHAR(4000) ;
742757

743758
RAISERROR (N'Setting up temporary tables for sp_BlitzCache',0,1) WITH NOWAIT;
@@ -756,6 +771,7 @@ BEGIN
756771
RETURN;
757772
END
758773

774+
SELECT @MinMemoryPerQuery = CONVERT(INT, c.value) FROM sys.configurations AS c WHERE c.name = 'min memory per query (KB)';
759775

760776
SET @SortOrder = LOWER(@SortOrder);
761777
SET @SortOrder = REPLACE(REPLACE(@SortOrder, 'average', 'avg'), '.', '');
@@ -1784,15 +1800,19 @@ UPDATE p
17841800
SET busy_loops = CASE WHEN (x.estimated_executions / 100.0) > x.estimated_rows THEN 1 END ,
17851801
tvf_join = CASE WHEN x.tvf_join = 1 THEN 1 END ,
17861802
warning_no_join_predicate = CASE WHEN x.no_join_warning = 1 THEN 1 END,
1787-
p.is_table_variable = CASE WHEN x.is_table_variable = 1 THEN 1 END
1803+
is_table_variable = CASE WHEN x.is_table_variable = 1 THEN 1 END,
1804+
no_stats_warning = CASE WHEN x.no_stats_warning = 1 THEN 1 END,
1805+
relop_warnings = CASE WHEN x.relop_warnings = 1 THEN 1 END
17881806
FROM ##bou_BlitzCacheProcs p
17891807
JOIN (
17901808
SELECT qs.SqlHandle,
17911809
relop.value('sum(/p:RelOp/@EstimateRows)', 'float') AS estimated_rows ,
17921810
relop.value('sum(/p:RelOp/@EstimateRewinds)', 'float') + relop.value('sum(/p:RelOp/@EstimateRebinds)', 'float') + 1.0 AS estimated_executions ,
17931811
relop.exist('/p:RelOp[contains(@LogicalOp, "Join")]/*/p:RelOp[(@LogicalOp[.="Table-valued function"])]') AS tvf_join,
17941812
relop.exist('/p:RelOp/p:Warnings[(@NoJoinPredicate[.="1"])]') AS no_join_warning,
1795-
relop.exist('/p:RelOp//*[local-name() = "Object"]/@Table[contains(., "@")]') AS is_table_variable
1813+
relop.exist('/p:RelOp//*[local-name() = "Object"]/@Table[contains(., "@")]') AS is_table_variable,
1814+
relop.exist('/p:RelOp/p:Warnings/p:ColumnsWithNoStatistics') AS no_stats_warning ,
1815+
relop.exist('/p:RelOp/p:Warnings') AS relop_warnings
17961816
FROM #relop qs
17971817
) AS x ON p.SqlHandle = x.SqlHandle
17981818
OPTION (RECOMPILE);
@@ -1819,7 +1839,7 @@ SET key_lookup_cost = x.key_lookup_cost
18191839
FROM (
18201840
SELECT
18211841
qs.SqlHandle,
1822-
relop.value('/p:RelOp[1]/@EstimatedTotalSubtreeCost', 'float') AS key_lookup_cost
1842+
relop.value('sum(/p:RelOp/@EstimatedTotalSubtreeCost)', 'float') AS key_lookup_cost
18231843
FROM #relop qs
18241844
WHERE [relop].exist('/p:RelOp/p:IndexScan[(@Lookup[.="1"])]') = 1
18251845
) AS x
@@ -1833,7 +1853,7 @@ SET remote_query_cost = x.remote_query_cost
18331853
FROM (
18341854
SELECT
18351855
qs.SqlHandle,
1836-
relop.value('/p:RelOp[1]/@EstimatedTotalSubtreeCost', 'float') AS remote_query_cost
1856+
relop.value('sum(/p:RelOp/@EstimatedTotalSubtreeCost)', 'float') AS remote_query_cost
18371857
FROM #relop qs
18381858
WHERE [relop].exist('/p:RelOp[(@PhysicalOp[.="Remote Query"])]') = 1
18391859
) AS x
@@ -1851,6 +1871,38 @@ ON b.QueryHash = qs.QueryHash
18511871
CROSS APPLY qs.statement.nodes('/p:StmtCursor') AS n1(fn)
18521872
OPTION (RECOMPILE) ;
18531873

1874+
;WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
1875+
UPDATE b
1876+
SET
1877+
b.is_table_scan = x.is_table_scan,
1878+
b.backwards_scan = x.backwards_scan,
1879+
b.forced_index = x.forced_index,
1880+
b.forced_seek = x.forced_seek,
1881+
b.forced_scan = x.forced_scan
1882+
FROM ##bou_BlitzCacheProcs b
1883+
JOIN (
1884+
SELECT
1885+
qs.SqlHandle,
1886+
0 AS is_table_scan,
1887+
q.n.exist('@ScanDirection[.="BACKWARD"]') AS backwards_scan,
1888+
q.n.value('@ForcedIndex', 'bit') AS forced_index,
1889+
q.n.value('@ForceSeek', 'bit') AS forced_seek,
1890+
q.n.value('@ForceScan', 'bit') AS forced_scan
1891+
FROM #relop qs
1892+
CROSS APPLY qs.relop.nodes('//p:IndexScan') AS q(n)
1893+
UNION ALL
1894+
SELECT
1895+
qs.SqlHandle,
1896+
1 AS is_table_scan,
1897+
q.n.exist('@ScanDirection[.="BACKWARD"]') AS backwards_scan,
1898+
q.n.value('@ForcedIndex', 'bit') AS forced_index,
1899+
q.n.value('@ForceSeek', 'bit') AS forced_seek,
1900+
q.n.value('@ForceScan', 'bit') AS forced_scan
1901+
FROM #relop qs
1902+
CROSS APPLY qs.relop.nodes('//p:TableScan') AS q(n)
1903+
) AS x ON b.SqlHandle = x.SqlHandle
1904+
OPTION (RECOMPILE) ;
1905+
18541906

18551907
IF @v >= 12
18561908
BEGIN
@@ -2051,7 +2103,7 @@ SET frequent_execution = CASE WHEN ExecutionsPerMinute > @execution_threshold
20512103
is_key_lookup_expensive = CASE WHEN QueryPlanCost > (@ctp / 2) AND key_lookup_cost >= QueryPlanCost * .5 THEN 1 END,
20522104
is_remote_query_expensive = CASE WHEN remote_query_cost >= QueryPlanCost * .05 THEN 1 END,
20532105
is_forced_serial = CASE WHEN is_forced_serial = 1 AND QueryPlanCost > (@ctp / 2) THEN 1 END,
2054-
is_unused_grant = CASE WHEN PercentMemoryGrantUsed <= @memory_grant_warning_percent AND MinGrantKB > 0 THEN 1 END
2106+
is_unused_grant = CASE WHEN PercentMemoryGrantUsed <= @memory_grant_warning_percent AND MinGrantKB > @MinMemoryPerQuery THEN 1 END
20552107
OPTION (RECOMPILE) ;
20562108

20572109

@@ -2126,7 +2178,14 @@ SET Warnings = SUBSTRING(
21262178
CASE WHEN function_count > 0 THEN ', Calls ' + CONVERT(VARCHAR(10), function_count) + ' function(s)' ELSE '' END +
21272179
CASE WHEN clr_function_count > 0 THEN ', Calls ' + CONVERT(VARCHAR(10), clr_function_count) + ' CLR function(s)' ELSE '' END +
21282180
CASE WHEN PlanCreationTimeHours <= 4 THEN ', Plan created last 4hrs' ELSE '' END +
2129-
CASE WHEN is_table_variable = 1 THEN ', Table Variables' ELSE '' END
2181+
CASE WHEN is_table_variable = 1 THEN ', Table Variables' ELSE '' END +
2182+
CASE WHEN no_stats_warning = 1 THEN ', Columns With No Statistics' ELSE '' END +
2183+
CASE WHEN relop_warnings = 1 THEN ', Operator Warnings' ELSE '' END +
2184+
CASE WHEN is_table_scan = 1 THEN ', Table Scans' ELSE '' END +
2185+
CASE WHEN backwards_scan = 1 THEN ', Backwards Scans' ELSE '' END +
2186+
CASE WHEN forced_index = 1 THEN ', Forced Indexes' ELSE '' END +
2187+
CASE WHEN forced_seek = 1 THEN ', Forced Seeks' ELSE '' END +
2188+
CASE WHEN forced_scan = 1 THEN ', Forced Scans' ELSE '' END
21302189
, 2, 200000)
21312190
OPTION (RECOMPILE) ;
21322191

@@ -2135,12 +2194,6 @@ SET Warnings = SUBSTRING(
21352194

21362195

21372196

2138-
2139-
2140-
2141-
2142-
2143-
21442197
Results:
21452198
IF @OutputDatabaseName IS NOT NULL
21462199
AND @OutputSchemaName IS NOT NULL
@@ -2440,7 +2493,13 @@ BEGIN
24402493
CASE WHEN function_count > 0 IS NOT NULL THEN '', 31'' ELSE '''' END +
24412494
CASE WHEN clr_function_count > 0 THEN '', 32'' ELSE '''' END +
24422495
CASE WHEN PlanCreationTimeHours <= 4 THEN '', 33'' ELSE '''' END +
2443-
CASE WHEN is_table_variable = 1 then '', 34'' ELSE '''' END
2496+
CASE WHEN is_table_variable = 1 THEN '', 34'' ELSE '''' END +
2497+
CASE WHEN no_stats_warning = 1 THEN '', 35'' ELSE '''' END +
2498+
CASE WHEN relop_warnings = 1 THEN '', 36'' ELSE '''' END +
2499+
CASE WHEN is_table_scan = 1 THEN '', 37'' ELSE '''' END +
2500+
CASE WHEN backwards_scan = 1 THEN '', 38'' ELSE '''' END +
2501+
CASE WHEN forced_index = 1 THEN '', 39'' ELSE '''' END +
2502+
CASE WHEN forced_seek = 1 OR forced_scan = 1 THEN '', 40'' ELSE '''' END
24442503
, 2, 200000) AS opserver_warning , ' + @nl ;
24452504
END
24462505

@@ -2902,7 +2961,7 @@ BEGIN
29022961
100,
29032962
'Unused memory grants',
29042963
'Queries are asking for more memory than they''re using',
2905-
'No URL yet.',
2964+
'https://www.brentozar.com/blitzcache/unused-memory-grants/',
29062965
'Queries have large unused memory grants. This can cause concurrency issues, if queries are waiting a long time to get memory to run.') ;
29072966

29082967
IF EXISTS (SELECT 1/0
@@ -2915,7 +2974,7 @@ BEGIN
29152974
100,
29162975
'Compute Scalar That References A Function',
29172976
'This could be trouble if you''re using Scalar Functions or MSTVFs',
2918-
'No URL yet.',
2977+
'https://www.brentozar.com/blitzcache/compute-scalar-functions/',
29192978
'Both of these will force queries to run serially, run at least once per row, and may result in poor cardinality estimates') ;
29202979

29212980
IF EXISTS (SELECT 1/0
@@ -2928,7 +2987,7 @@ BEGIN
29282987
100,
29292988
'Compute Scalar That References A CLR Function',
29302989
'This could be trouble if your CLR functions perform data access',
2931-
'No URL yet.',
2990+
'https://www.brentozar.com/blitzcache/compute-scalar-functions/',
29322991
'May force queries to run serially, run at least once per row, and may result in poor cardinlity estimates') ;
29332992

29342993

@@ -2942,9 +3001,89 @@ BEGIN
29423001
100,
29433002
'Table Variables detected',
29443003
'Beware nasty side effects',
2945-
'No URL yet.',
3004+
'https://www.brentozar.com/blitzcache/table-variables/',
29463005
'All modifications are single threaded, and selects have really low row estimates.') ;
29473006

3007+
IF EXISTS (SELECT 1/0
3008+
FROM ##bou_BlitzCacheProcs p
3009+
WHERE p.no_stats_warning = 1
3010+
AND SPID = @@SPID)
3011+
INSERT INTO ##bou_BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
3012+
VALUES (@@SPID,
3013+
35,
3014+
100,
3015+
'Columns with no statistics',
3016+
'Poor cardinality estimates may ensue',
3017+
'https://www.brentozar.com/blitzcache/columns-no-statistics/',
3018+
'Sometimes this happens with indexed views, other times because auto create stats is turned off.') ;
3019+
3020+
IF EXISTS (SELECT 1/0
3021+
FROM ##bou_BlitzCacheProcs p
3022+
WHERE p.relop_warnings = 1
3023+
AND SPID = @@SPID)
3024+
INSERT INTO ##bou_BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
3025+
VALUES (@@SPID,
3026+
36,
3027+
100,
3028+
'Operator Warnings',
3029+
'SQL is throwing operator level plan warnings',
3030+
'http://brentozar.com/blitzcache/query-plan-warnings/',
3031+
'Check the plan for more details.') ;
3032+
3033+
IF EXISTS (SELECT 1/0
3034+
FROM ##bou_BlitzCacheProcs p
3035+
WHERE p.is_table_scan = 1
3036+
AND SPID = @@SPID)
3037+
INSERT INTO ##bou_BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
3038+
VALUES (@@SPID,
3039+
37,
3040+
100,
3041+
'Table Scans',
3042+
'Your database has HEAPs',
3043+
'https://www.brentozar.com/archive/2012/05/video-heaps/',
3044+
'This may not be a problem. Run sp_BlitzIndex for more information.') ;
3045+
3046+
IF EXISTS (SELECT 1/0
3047+
FROM ##bou_BlitzCacheProcs p
3048+
WHERE p.backwards_scan = 1
3049+
AND SPID = @@SPID)
3050+
INSERT INTO ##bou_BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
3051+
VALUES (@@SPID,
3052+
38,
3053+
100,
3054+
'Backwards Scans',
3055+
'Indexes are being read backwards',
3056+
'https://www.brentozar.com/blitzcache/backwards-scans/',
3057+
'This isn''t always a problem. They can cause serial zones in plans, and may need an index to match sort order.') ;
3058+
3059+
IF EXISTS (SELECT 1/0
3060+
FROM ##bou_BlitzCacheProcs p
3061+
WHERE p.forced_index = 1
3062+
AND SPID = @@SPID)
3063+
INSERT INTO ##bou_BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
3064+
VALUES (@@SPID,
3065+
39,
3066+
100,
3067+
'Index forcing',
3068+
'Someone is using hints to force index usage',
3069+
'https://www.brentozar.com/blitzcache/optimizer-forcing/',
3070+
'This can cause inefficient plans, and will prevent missing index requests.') ;
3071+
3072+
IF EXISTS (SELECT 1/0
3073+
FROM ##bou_BlitzCacheProcs p
3074+
WHERE p.forced_seek = 1
3075+
OR p.forced_scan = 1
3076+
AND SPID = @@SPID)
3077+
INSERT INTO ##bou_BlitzCacheResults (SPID, CheckID, Priority, FindingsGroup, Finding, URL, Details)
3078+
VALUES (@@SPID,
3079+
40,
3080+
100,
3081+
'Seek/Scan forcing',
3082+
'Someone is using hints to force index seeks/scans',
3083+
'https://www.brentozar.com/blitzcache/optimizer-forcing/',
3084+
'This can cause inefficient plans by taking seek vs scan choice away from the optimizer.') ;
3085+
3086+
29483087
IF EXISTS (SELECT 1/0
29493088
FROM #plan_creation p
29503089
WHERE p.percent_24 > 0
@@ -2956,11 +3095,10 @@ BEGIN
29563095
254,
29573096
'Plan Cache Information',
29583097
'You have ' + CONVERT(NVARCHAR(10), p.percent_24) + '% plans created in the past 24 hours, and ' + CONVERT(NVARCHAR(10), p.percent_4) + '% created in the past 4 hours.',
2959-
'No URL yet.',
3098+
'',
29603099
'If these percentages are high, it may be a sign of memory pressure or plan cache instability.'
29613100
FROM #plan_creation p ;
29623101

2963-
29643102
IF EXISTS (SELECT 1/0
29653103
FROM #trace_flags AS tf
29663104
WHERE tf.global_trace_flags IS NOT NULL

0 commit comments

Comments
 (0)