Skip to content

Commit b7add51

Browse files
Weight sp_QuickieStore regression comparator by count_executions
The regression_metric_average and current_metric_average SELECTs built their per-unit-work averages with AVG(qsrs.avg_cpu_time), AVG(qsrs.avg_duration), etc. — but qsrs.avg_* are themselves per-interval averages, so AVG over them is an unweighted mean of means. An interval with 1 execution at 1000 ms gets the same pull on the number as an interval with 10,000 executions at 10 ms. Regression detection then flags sparse outliers as movement, and misses real load changes that play out across many intervals with similar avg. Changed the per-unit-work cases (cpu, logical/physical reads, writes, duration, memory, tempdb, rows) in both the baseline and current SELECTs to: SUM(qsrs.avg_metric * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0) Left alone: - 'total *' cases — already SUM(avg * count), which matches intent. - 'executions' — qsrs.count_executions is a count, not an average, so plain AVG per interval is meaningful. - wait branch — waits.total_query_wait_time_ms is already a per-interval total, so AVG across intervals is the right shape. Verified sp_QuickieStore installs clean and runs against PerformanceMonitor on SQL Server 2022 with @regression_baseline_start_date/@regression_comparator = 'relative' /@regression_direction = 'regressed' without errors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e185df8 commit b7add51

1 file changed

Lines changed: 31 additions & 20 deletions

File tree

sp_QuickieStore/sp_QuickieStore.sql

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8873,22 +8873,29 @@ BEGIN
88738873
@sql += N'
88748874
SELECT
88758875
qsq.query_hash,
8876-
/* All of these but count_executions are already floats. */
8876+
/* All of these but count_executions are already floats.
8877+
qsrs.avg_* columns are themselves per-interval averages, so
8878+
AVG(avg_*) is an unweighted mean of means. Weight by
8879+
count_executions to get the true cross-interval average —
8880+
otherwise intervals with very few executions get the same
8881+
pull on the number as intervals with many, and regression
8882+
detection skews toward sparse outlier intervals. */
88778883
regression_metric_average =
88788884
CONVERT
88798885
(
88808886
float,
88818887
' +
88828888
CASE @sort_order
8883-
WHEN 'cpu' THEN N'AVG(qsrs.avg_cpu_time)'
8884-
WHEN 'logical reads' THEN N'AVG(qsrs.avg_logical_io_reads)'
8885-
WHEN 'physical reads' THEN N'AVG(qsrs.avg_physical_io_reads)'
8886-
WHEN 'writes' THEN N'AVG(qsrs.avg_logical_io_writes)'
8887-
WHEN 'duration' THEN N'AVG(qsrs.avg_duration)'
8888-
WHEN 'memory' THEN N'AVG(qsrs.avg_query_max_used_memory)'
8889-
WHEN 'tempdb' THEN CASE WHEN @new = 1 THEN N'AVG(qsrs.avg_tempdb_space_used)' ELSE N'AVG(qsrs.avg_cpu_time)' END
8889+
WHEN 'cpu' THEN N'SUM(qsrs.avg_cpu_time * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
8890+
WHEN 'logical reads' THEN N'SUM(qsrs.avg_logical_io_reads * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
8891+
WHEN 'physical reads' THEN N'SUM(qsrs.avg_physical_io_reads * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
8892+
WHEN 'writes' THEN N'SUM(qsrs.avg_logical_io_writes * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
8893+
WHEN 'duration' THEN N'SUM(qsrs.avg_duration * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
8894+
WHEN 'memory' THEN N'SUM(qsrs.avg_query_max_used_memory * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
8895+
WHEN 'tempdb' THEN CASE WHEN @new = 1 THEN N'SUM(qsrs.avg_tempdb_space_used * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)' ELSE N'SUM(qsrs.avg_cpu_time * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)' END
8896+
/* count_executions per interval is meaningful as a plain mean — it''s a count, not an average-of-averages. */
88908897
WHEN 'executions' THEN N'AVG(qsrs.count_executions)'
8891-
WHEN 'rows' THEN N'AVG(qsrs.avg_rowcount)'
8898+
WHEN 'rows' THEN N'SUM(qsrs.avg_rowcount * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
88928899
WHEN 'total cpu' THEN N'SUM(qsrs.avg_cpu_time * qsrs.count_executions)'
88938900
WHEN 'total logical reads' THEN N'SUM(qsrs.avg_logical_io_reads * qsrs.count_executions)'
88948901
WHEN 'total physical reads' THEN N'SUM(qsrs.avg_physical_io_reads * qsrs.count_executions)'
@@ -8897,7 +8904,8 @@ BEGIN
88978904
WHEN 'total memory' THEN N'SUM(qsrs.avg_query_max_used_memory * qsrs.count_executions)'
88988905
WHEN 'total tempdb' THEN CASE WHEN @new = 1 THEN N'SUM(qsrs.avg_tempdb_space_used * qsrs.count_executions)' ELSE N'SUM(qsrs.avg_cpu_time * qsrs.count_executions)' END
88998906
WHEN 'total rows' THEN N'SUM(qsrs.avg_rowcount * qsrs.count_executions)'
8900-
ELSE CASE WHEN @sort_order_is_a_wait = 1 THEN N'AVG(waits.total_query_wait_time_ms)' ELSE N'AVG(qsrs.avg_cpu_time)' END
8907+
/* Waits and the fallback path — waits are per-interval totals so AVG is correct; fallback mirrors cpu path. */
8908+
ELSE CASE WHEN @sort_order_is_a_wait = 1 THEN N'AVG(waits.total_query_wait_time_ms)' ELSE N'SUM(qsrs.avg_cpu_time * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)' END
89018909
END
89028910
+ N'
89038911
)
@@ -8985,22 +8993,25 @@ BEGIN
89858993
@sql += N'
89868994
SELECT
89878995
qsq.query_hash,
8988-
/* All of these but count_executions are already floats. */
8996+
/* All of these but count_executions are already floats.
8997+
Weighted by count_executions so the current-window average
8998+
matches the baseline-window computation (see baseline block
8999+
above) and regression percentages compare like with like. */
89899000
current_metric_average =
89909001
CONVERT
89919002
(
89929003
float,
89939004
' +
89949005
CASE @sort_order
8995-
WHEN 'cpu' THEN N'AVG(qsrs.avg_cpu_time)'
8996-
WHEN 'logical reads' THEN N'AVG(qsrs.avg_logical_io_reads)'
8997-
WHEN 'physical reads' THEN N'AVG(qsrs.avg_physical_io_reads)'
8998-
WHEN 'writes' THEN N'AVG(qsrs.avg_logical_io_writes)'
8999-
WHEN 'duration' THEN N'AVG(qsrs.avg_duration)'
9000-
WHEN 'memory' THEN N'AVG(qsrs.avg_query_max_used_memory)'
9001-
WHEN 'tempdb' THEN CASE WHEN @new = 1 THEN N'AVG(qsrs.avg_tempdb_space_used)' ELSE N'AVG(qsrs.avg_cpu_time)' END
9006+
WHEN 'cpu' THEN N'SUM(qsrs.avg_cpu_time * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
9007+
WHEN 'logical reads' THEN N'SUM(qsrs.avg_logical_io_reads * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
9008+
WHEN 'physical reads' THEN N'SUM(qsrs.avg_physical_io_reads * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
9009+
WHEN 'writes' THEN N'SUM(qsrs.avg_logical_io_writes * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
9010+
WHEN 'duration' THEN N'SUM(qsrs.avg_duration * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
9011+
WHEN 'memory' THEN N'SUM(qsrs.avg_query_max_used_memory * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
9012+
WHEN 'tempdb' THEN CASE WHEN @new = 1 THEN N'SUM(qsrs.avg_tempdb_space_used * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)' ELSE N'SUM(qsrs.avg_cpu_time * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)' END
90029013
WHEN 'executions' THEN N'AVG(qsrs.count_executions)'
9003-
WHEN 'rows' THEN N'AVG(qsrs.avg_rowcount)'
9014+
WHEN 'rows' THEN N'SUM(qsrs.avg_rowcount * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)'
90049015
WHEN 'total cpu' THEN N'SUM(qsrs.avg_cpu_time * qsrs.count_executions)'
90059016
WHEN 'total logical reads' THEN N'SUM(qsrs.avg_logical_io_reads * qsrs.count_executions)'
90069017
WHEN 'total physical reads' THEN N'SUM(qsrs.avg_physical_io_reads * qsrs.count_executions)'
@@ -9009,7 +9020,7 @@ BEGIN
90099020
WHEN 'total memory' THEN N'SUM(qsrs.avg_query_max_used_memory * qsrs.count_executions)'
90109021
WHEN 'total tempdb' THEN CASE WHEN @new = 1 THEN N'SUM(qsrs.avg_tempdb_space_used * qsrs.count_executions)' ELSE N'SUM(qsrs.avg_cpu_time * qsrs.count_executions)' END
90119022
WHEN 'total rows' THEN N'SUM(qsrs.avg_rowcount * qsrs.count_executions)'
9012-
ELSE CASE WHEN @sort_order_is_a_wait = 1 THEN N'AVG(waits.total_query_wait_time_ms)' ELSE N'AVG(qsrs.avg_cpu_time)' END
9023+
ELSE CASE WHEN @sort_order_is_a_wait = 1 THEN N'AVG(waits.total_query_wait_time_ms)' ELSE N'SUM(qsrs.avg_cpu_time * qsrs.count_executions) / NULLIF(SUM(CONVERT(float, qsrs.count_executions)), 0)' END
90139024
END
90149025
+ N'
90159026
)

0 commit comments

Comments
 (0)