@@ -1731,6 +1731,14 @@ private static long GetEffectiveChildElapsedMs(PlanNode child)
17311731 if ( child . PhysicalOp == "Parallelism" && child . Children . Count > 0 )
17321732 return child . Children . Max ( GetEffectiveChildElapsedMs ) ;
17331733
1734+ // Batch mode pipelines — each operator's elapsed stands alone rather than
1735+ // rolling up its descendants the way row-mode does. For a parent computing
1736+ // self-time above a batch-mode subtree, subtract the whole pipeline's time
1737+ // (Joe #215 D1: Parallelism gather-streams above three batch operators).
1738+ var mode = child . ActualExecutionMode ?? child . ExecutionMode ;
1739+ if ( mode == "Batch" && child . HasActualStats )
1740+ return SumBatchSubtreeElapsedMs ( child ) ;
1741+
17341742 // Child has its own stats: use them
17351743 if ( child . ActualElapsedMs > 0 )
17361744 return child . ActualElapsedMs ;
@@ -1745,6 +1753,30 @@ private static long GetEffectiveChildElapsedMs(PlanNode child)
17451753 return sum ;
17461754 }
17471755
1756+ /// <summary>
1757+ /// Sums ActualElapsedMs across a contiguous batch-mode subtree (stops at
1758+ /// Parallelism exchange zone boundaries). Batch operators pipeline — elapsed
1759+ /// times are standalone, not cumulative — so summing gives the total work the
1760+ /// zone did, which is what a row-mode parent above the zone should subtract
1761+ /// to get its own self-time.
1762+ /// </summary>
1763+ private static long SumBatchSubtreeElapsedMs ( PlanNode node )
1764+ {
1765+ long sum = node . ActualElapsedMs ;
1766+ foreach ( var child in node . Children )
1767+ {
1768+ // Zone boundary — stop summing
1769+ if ( child . PhysicalOp == "Parallelism" ) continue ;
1770+
1771+ var childMode = child . ActualExecutionMode ?? child . ExecutionMode ;
1772+ if ( childMode == "Batch" && child . HasActualStats )
1773+ sum += SumBatchSubtreeElapsedMs ( child ) ;
1774+ else
1775+ sum += GetEffectiveChildElapsedMs ( child ) ;
1776+ }
1777+ return sum ;
1778+ }
1779+
17481780 /// <summary>
17491781 /// Calculates a Parallelism (exchange) operator's own elapsed time.
17501782 /// Exchange times are unreliable — they accumulate wait time caused by
0 commit comments