|
72 | 72 | SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; |
73 | 73 |
|
74 | 74 | SELECT |
75 | | - @version = '3.4', |
76 | | - @version_date = '20260401'; |
| 75 | + @version = '3.5', |
| 76 | + @version_date = '20260420'; |
77 | 77 |
|
78 | 78 | IF @help = 1 |
79 | 79 | BEGIN |
@@ -435,15 +435,24 @@ AND ca.utc_timestamp < @end_date'; |
435 | 435 | END; |
436 | 436 | ELSE |
437 | 437 | BEGIN |
438 | | - /* 2017+ handling */ |
| 438 | + /* |
| 439 | + 2017+ handling. Use the same half-open (>= @start_date AND |
| 440 | + < @end_date) shape as the pre-2017 branch so an event captured |
| 441 | + at exactly @end_date is not included on 2017+ while excluded |
| 442 | + on pre-2017 — previously BETWEEN meant a closed interval on |
| 443 | + 2017+ and a row at the boundary could appear or not depending |
| 444 | + on which branch ran. |
| 445 | + */ |
439 | 446 | SET @cross_apply = N'CROSS APPLY xml.{object_name}.nodes(''/event'') AS e(x)'; |
440 | 447 |
|
441 | 448 | IF @timestamp_utc_mode = 1 |
442 | 449 | SET @time_filter = N' |
443 | | - AND CONVERT(datetimeoffset(7), fx.timestamp_utc) BETWEEN @start_date AND @end_date'; |
| 450 | + AND CONVERT(datetimeoffset(7), fx.timestamp_utc) >= @start_date |
| 451 | + AND CONVERT(datetimeoffset(7), fx.timestamp_utc) < @end_date'; |
444 | 452 | ELSE |
445 | 453 | SET @time_filter = N' |
446 | | - AND fx.timestamp_utc BETWEEN @start_date AND @end_date'; |
| 454 | + AND fx.timestamp_utc >= @start_date |
| 455 | + AND fx.timestamp_utc < @end_date'; |
447 | 456 | END; |
448 | 457 |
|
449 | 458 | SET @sql_template = |
@@ -2218,7 +2227,21 @@ AND ca.utc_timestamp < @end_date'; |
2218 | 2227 | ), |
2219 | 2228 | tc.wait_type, |
2220 | 2229 | waits = SUM(CONVERT(bigint, tc.waits)), |
2221 | | - average_wait_time_ms = CONVERT(bigint, AVG(tc.average_wait_time_ms)), |
| 2230 | + /* |
| 2231 | + Weighted average rather than AVG(avg): tc.average_wait_time_ms |
| 2232 | + is already a per-event average, so AVG() over the bucket was |
| 2233 | + an unweighted mean of means — events with one wait got the |
| 2234 | + same pull on the output as events with thousands. Weight by |
| 2235 | + waits to get the true bucket-scoped average. NULLIF keeps us |
| 2236 | + safe if every contributing row had waits = 0. |
| 2237 | + */ |
| 2238 | + average_wait_time_ms = |
| 2239 | + CONVERT |
| 2240 | + ( |
| 2241 | + bigint, |
| 2242 | + SUM(CONVERT(decimal(38, 2), tc.average_wait_time_ms) * CONVERT(decimal(38, 2), tc.waits)) |
| 2243 | + / NULLIF(SUM(CONVERT(decimal(38, 2), tc.waits)), 0) |
| 2244 | + ), |
2222 | 2245 | max_wait_time_ms = CONVERT(bigint, MAX(tc.max_wait_time_ms)) |
2223 | 2246 | INTO #tc |
2224 | 2247 | FROM #topwaits_count AS tc |
@@ -2951,7 +2974,11 @@ AND ca.utc_timestamp < @end_date'; |
2951 | 2974 | CROSS APPLY wi.sp_server_diagnostics_component_result.nodes('/event') AS w(x) |
2952 | 2975 | WHERE w.x.exist('(data[@name="component"]/text[.= "QUERY_PROCESSING"])') = 1 |
2953 | 2976 | AND (w.x.exist('(data[@name="state"]/text[.= "WARNING"])') = @warnings_only OR @warnings_only = 0) |
2954 | | - AND (w.x.exist('(/event/data[@name="data"]/value/queryProcessing/@pendingTasks[.>= sql:variable("@pending_task_threshold")])') = 1 OR @warnings_only = 0) |
| 2977 | + /* Threshold is honored whether or not @warnings_only is set — the |
| 2978 | + parameter documents "minimum pending tasks to display" and the |
| 2979 | + previous `OR @warnings_only = 0` short-circuit silently ignored |
| 2980 | + the user-supplied value whenever warnings-only was off. */ |
| 2981 | + AND w.x.exist('(/event/data[@name="data"]/value/queryProcessing/@pendingTasks[.>= sql:variable("@pending_task_threshold")])') = 1 |
2955 | 2982 | OPTION(RECOMPILE, MAXDOP 1); |
2956 | 2983 |
|
2957 | 2984 | IF @debug = 1 |
@@ -3176,7 +3203,10 @@ AND ca.utc_timestamp < @end_date'; |
3176 | 3203 | INTO #pending_task_details |
3177 | 3204 | FROM #sp_server_diagnostics_component_result AS wi |
3178 | 3205 | CROSS APPLY wi.sp_server_diagnostics_component_result.nodes('/event') AS w(x) |
3179 | | - CROSS APPLY w.x.nodes('/event/data[@name="data"]/value/queryProcessing[@pendingTasks > 1]/pendingTasks/entryPoint') AS ep(e) |
| 3206 | + /* Hardcoded threshold > 1 ignored the @pending_task_threshold |
| 3207 | + parameter. Replaced with sql:variable() binding so the user's |
| 3208 | + value actually takes effect here too. */ |
| 3209 | + CROSS APPLY w.x.nodes('/event/data[@name="data"]/value/queryProcessing[@pendingTasks >= sql:variable("@pending_task_threshold")]/pendingTasks/entryPoint') AS ep(e) |
3180 | 3210 | WHERE w.x.exist('(data[@name="component"]/text[.= "QUERY_PROCESSING"])') = 1 |
3181 | 3211 | AND (w.x.exist('(data[@name="state"]/text[.= "WARNING"])') = @warnings_only OR @warnings_only = 0) |
3182 | 3212 | OPTION(RECOMPILE, MAXDOP 1); |
@@ -5721,9 +5751,16 @@ AND ca.utc_timestamp < @end_date'; |
5721 | 5751 | FROM #deadlocks AS d |
5722 | 5752 | CROSS APPLY d.xml_deadlock_report.nodes('//deadlock/process-list/process') AS e(x) |
5723 | 5753 | ) AS x |
| 5754 | + /* Standard "filter if supplied, pass-through if NULL" predicate |
| 5755 | + pairs must be combined with AND between the groups — OR let |
| 5756 | + rows through whenever either parameter was NULL, which makes |
| 5757 | + the @database_name/@dbid filter loose whenever only one side |
| 5758 | + was supplied. Currently masked because the validation block |
| 5759 | + above aborts when the two disagree, but the shape was |
| 5760 | + wrong and would break if that validation ever relaxed. */ |
5724 | 5761 | WHERE (x.database_id = @dbid |
5725 | 5762 | OR @dbid IS NULL) |
5726 | | - OR (x.current_database_name = @database_name |
| 5763 | + AND (x.current_database_name = @database_name |
5727 | 5764 | OR @database_name IS NULL) |
5728 | 5765 | OPTION(RECOMPILE, MAXDOP 1); |
5729 | 5766 |
|
|
0 commit comments