@@ -1375,7 +1375,32 @@ BEGIN
13751375 EXEC sp_executesql @sql, N ' @Top INT, @min_duration INT' , @Top, @DurationFilter_i;
13761376END
13771377
1378-
1378+ /* Update ##bou_BlitzCacheProcs to get Stored Proc info
1379+ * This should get totals for all statements in a Stored Proc
1380+ */
1381+ ;WITH agg AS (
1382+ SELECT
1383+ b .SqlHandle ,
1384+ SUM (b .MinReturnedRows ) AS MinReturnedRows,
1385+ SUM (b .MaxReturnedRows ) AS MaxReturnedRows,
1386+ SUM (b .AverageReturnedRows ) AS AverageReturnedRows,
1387+ SUM (b .TotalReturnedRows ) AS TotalReturnedRows,
1388+ SUM (b .LastReturnedRows ) AS LastReturnedRows
1389+ FROM ##bou_BlitzCacheProcs b
1390+ WHERE b .QueryHash IS NOT NULL
1391+ GROUP BY b .SqlHandle
1392+ )
1393+ UPDATE b
1394+ SET
1395+ b .MinReturnedRows = b2 .MinReturnedRows ,
1396+ b .MaxReturnedRows = b2 .MaxReturnedRows ,
1397+ b .AverageReturnedRows = b2 .AverageReturnedRows ,
1398+ b .TotalReturnedRows = b2 .TotalReturnedRows ,
1399+ b .LastReturnedRows = b2 .LastReturnedRows
1400+ FROM ##bou_BlitzCacheProcs b
1401+ JOIN agg b2
1402+ ON b2 .SqlHandle = b .SqlHandle
1403+ WHERE b .QueryHash IS NULL
13791404
13801405/* Compute the total CPU, etc across our active set of the plan cache.
13811406 * Yes, there's a flaw - this doesn't include anything outside of our @Top
@@ -1520,6 +1545,7 @@ OPTION (RECOMPILE) ;
15201545WITH XMLNAMESPACES(' http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
15211546SELECT QueryHash ,
15221547 SqlHandle ,
1548+ PlanHandle,
15231549 q .n .query(' .' ) AS statement
15241550INTO #statements
15251551FROM ##bou_BlitzCacheProcs p
@@ -1559,50 +1585,55 @@ WHERE ##bou_BlitzCacheProcs.QueryHash = x.QueryHash
15591585OPTION (RECOMPILE ) ;
15601586
15611587-- statement level checks
1588+ WITH XMLNAMESPACES(' http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
1589+ UPDATE ##bou_BlitzCacheProcs
1590+ SET QueryPlanCost = CASE WHEN QueryType LIKE ' %Stored Procedure%' THEN
1591+ statement .value (' sum(/p:StmtSimple/@StatementSubTreeCost)' , ' float' )
1592+ ELSE
1593+ statement .value (' sum(/p:StmtSimple[xs:hexBinary(substring(@QueryPlanHash, 3)) = xs:hexBinary(sql:column("QueryPlanHash"))]/@StatementSubTreeCost)' , ' float' )
1594+ END ,
1595+ compile_timeout = CASE WHEN statement .exist(' /p:StmtSimple/@StatementOptmEarlyAbortReason[.="TimeOut"]' ) = 1 THEN 1 END ,
1596+ compile_memory_limit_exceeded = CASE WHEN statement .exist(' /p:StmtSimple/@StatementOptmEarlyAbortReason[.="MemoryLimitExceeded"]' ) = 1 THEN 1 END ,
1597+ unmatched_index_count = statement .value (' count(//p:UnmatchedIndexes/Parameterization/Object)' , ' int' ) ,
1598+ is_trivial = CASE WHEN statement .exist(' /p:StmtSimple[@StatementOptmLevel[.="TRIVIAL"]]/p:QueryPlan/p:ParameterList' ) = 1 THEN 1 END ,
1599+ unparameterized_query = CASE WHEN statement .exist(' //p:StmtSimple[@StatementOptmLevel[.="FULL"]]/p:QueryPlan/p:ParameterList' ) = 1 AND
1600+ statement .exist(' //p:StmtSimple[@StatementOptmLevel[.="FULL"]]/p:QueryPlan/p:ParameterList/p:ColumnReference' ) = 0 THEN 1
1601+ WHEN statement .exist(' //p:StmtSimple[@StatementOptmLevel[.="FULL"]]/p:QueryPlan/p:ParameterList' ) = 0 AND
1602+ statement .exist(' //p:StmtSimple[@StatementOptmLevel[.="FULL"]]/*/p:RelOp/descendant::p:ScalarOperator/p:Identifier/p:ColumnReference[contains(@Column, "@")]' ) = 1 THEN 1
1603+ END
1604+ FROM #statements s
1605+ WHERE s .QueryHash = ##bou_BlitzCacheProcs .QueryHash
1606+ OPTION (RECOMPILE );
1607+
1608+ -- Gather Stored Proc costs
15621609;WITH XMLNAMESPACES(' http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
1563- , c1 AS (
1564- SELECT
1565- QueryPlanCost_check = CASE WHEN QueryType LIKE ' %Stored Procedure%' THEN
1566- statement .value (' sum(/p:StmtSimple/@StatementSubTreeCost)' , ' float' )
1567- ELSE
1568- statement .value (' sum(/p:StmtSimple[xs:hexBinary(substring(@QueryPlanHash, 3)) = xs:hexBinary(sql:column("QueryPlanHash"))]/@StatementSubTreeCost)' , ' float' )
1569- END ,
1570- compile_timeout_check = CASE WHEN statement .exist(' /p:StmtSimple/@StatementOptmEarlyAbortReason[.="TimeOut"]' ) = 1 THEN 1 END ,
1571- compile_memory_limit_exceeded_check = CASE WHEN statement .exist(' /p:StmtSimple/@StatementOptmEarlyAbortReason[.="MemoryLimitExceeded"]' ) = 1 THEN 1 END ,
1572- unmatched_index_count_check = statement .value (' count(//p:UnmatchedIndexes/Parameterization/Object)' , ' int' ) ,
1573- is_trivial_check = CASE WHEN statement .exist(' /p:StmtSimple[@StatementOptmLevel[.="TRIVIAL"]]/p:QueryPlan/p:ParameterList' ) = 1 THEN 1 END ,
1574- unparameterized_query_check = CASE WHEN statement .exist(' //p:StmtSimple[@StatementOptmLevel[.="FULL"]]/p:QueryPlan/p:ParameterList' ) = 1 AND
1575- statement .exist(' //p:StmtSimple[@StatementOptmLevel[.="FULL"]]/p:QueryPlan/p:ParameterList/p:ColumnReference' ) = 0 THEN 1
1576- WHEN statement .exist(' //p:StmtSimple[@StatementOptmLevel[.="FULL"]]/p:QueryPlan/p:ParameterList' ) = 0 AND
1577- statement .exist(' //p:StmtSimple[@StatementOptmLevel[.="FULL"]]/*/p:RelOp/descendant::p:ScalarOperator/p:Identifier/p:ColumnReference[contains(@Column, "@")]' ) = 1 THEN 1
1578- END ,
1579- s .QueryHash
1580- FROM #statements s
1581- JOIN ##bou_BlitzCacheProcs b
1582- ON b .QueryHash = s .QueryHash
1583- ), c2 AS (
1584- SELECT
1585- QueryHash,
1586- QueryPlanCost = MAX ([c1].[QueryPlanCost_check]) ,
1587- compile_timeout = MAX ([c1].[compile_timeout_check]) ,
1588- compile_memory_limit_exceeded = MAX ([c1].[compile_memory_limit_exceeded_check]) ,
1589- unmatched_index_count = MAX ([c1].[unmatched_index_count_check]) ,
1590- is_trivial = MAX ([c1].[is_trivial_check]) ,
1591- unparameterized_query = MAX ([c1].[unparameterized_query_check])
1592- FROM c1
1593- GROUP BY QueryHash
1610+ , QueryCost AS (
1611+ SELECT
1612+ statement .value (' sum(/p:StmtSimple/@StatementSubTreeCost)' , ' float' ) AS SubTreeCost,
1613+ s .PlanHandle ,
1614+ s .SqlHandle
1615+ FROM #statements AS s
1616+ WHERE PlanHandle IS NOT NULL
15941617)
1595- UPDATE b
1596- SET
1597- b .QueryPlanCost = c2 .QueryPlanCost ,
1598- b .compile_timeout = c2 .compile_timeout ,
1599- b .compile_memory_limit_exceeded = c2 .compile_memory_limit_exceeded ,
1600- b .unmatched_index_count = c2 .unmatched_index_count ,
1601- b .is_trivial = c2 .is_trivial ,
1602- b .unparameterized_query = c2 .unparameterized_query
1603- FROM [c2] AS c2
1604- JOIN ##bou_BlitzCacheProcs AS b
1605- ON c2 .QueryHash = b .QueryHash
1618+ , QueryCostUpdate AS (
1619+ SELECT
1620+ SUM (qc .SubTreeCost ) OVER (PARTITION BY SqlHandle, PlanHandle) PlanTotalQuery,
1621+ qc .PlanHandle ,
1622+ qc .SqlHandle
1623+ FROM QueryCost qc
1624+ WHERE qc .SubTreeCost > 0
1625+ )
1626+ UPDATE b
1627+ SET b .QueryPlanCost =
1628+ CASE WHEN
1629+ b .QueryType LIKE ' %Procedure%' THEN
1630+ (SELECT TOP 1 PlanTotalQuery FROM QueryCostUpdate qcu WHERE qcu .PlanHandle = b .PlanHandle ORDER BY PlanTotalQuery DESC )
1631+ ELSE
1632+ b .QueryPlanCost
1633+ END
1634+ FROM QueryCostUpdate qcu
1635+ JOIN ##bou_BlitzCacheProcs AS b
1636+ ON qcu .SqlHandle = b .SqlHandle
16061637OPTION (RECOMPILE );
16071638
16081639-- query level checks
@@ -1684,12 +1715,12 @@ WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS
16841715UPDATE ##bou_BlitzCacheProcs
16851716SET NumberOfDistinctPlans = distinct_plan_count,
16861717 NumberOfPlans = number_of_plans,
1687- QueryPlanCost = CASE WHEN QueryType LIKE ' %Stored Procedure%' THEN
1688- QueryPlan . value ( ' sum(//p:StmtSimple/@StatementSubTreeCost)' , ' float' )
1718+ QueryPlanCost = CASE WHEN QueryType LIKE ' %Procedure%' THEN
1719+ QueryPlanCost
16891720 ELSE
16901721 QueryPlan .value (' sum(//p:StmtSimple[xs:hexBinary(substring(@QueryPlanHash, 3)) = xs:hexBinary(sql:column("QueryPlanHash"))]/@StatementSubTreeCost)' , ' float' )
16911722 END ,
1692- missing_index_count = QueryPlan .value (' count(//p:MissingIndexGroup)' , ' int' ) ,
1723+ missing_index_count = QueryPlan .value (' count(//p:MissingIndexGroup)' , ' int' ) ,
16931724 unmatched_index_count = QueryPlan .value (' count(//p:UnmatchedIndexes/p:Parameterization/p:Object)' , ' int' ) ,
16941725 plan_multiple_plans = CASE WHEN distinct_plan_count < number_of_plans THEN 1 END ,
16951726 is_trivial = CASE WHEN QueryPlan .exist (' //p:StmtSimple[@StatementOptmLevel[.="TRIVIAL"]]/p:QueryPlan/p:ParameterList' ) = 1 THEN 1 END ,
0 commit comments