|
1 | | --- Compile Date: 04/04/2026 15:20:33 UTC |
| 1 | +-- Compile Date: 04/04/2026 15:56:27 UTC |
2 | 2 | SET ANSI_NULLS ON; |
3 | 3 | SET ANSI_PADDING ON; |
4 | 4 | SET ANSI_WARNINGS ON; |
@@ -36462,6 +36462,8 @@ ALTER PROCEDURE |
36462 | 36462 | @minimum_execution_count bigint = 2, /*noise floor for single-exec queries*/ |
36463 | 36463 | @ignore_system_databases bit = 1, /*exclude master, model, msdb, tempdb*/ |
36464 | 36464 | @impact_threshold decimal(3, 2) = 0.50, /*minimum impact_score to surface (0.00-1.00)*/ |
| 36465 | + @find_single_use_plans bit = 0, /*show single-use plans consuming the most memory*/ |
| 36466 | + @find_duplicate_plans bit = 0, /*show query hashes with multiple cached plans*/ |
36465 | 36467 | @debug bit = 0, /*print diagnostics*/ |
36466 | 36468 | @help bit = 0, /*display parameter help*/ |
36467 | 36469 | @version varchar(30) = NULL OUTPUT, /*OUTPUT; for support*/ |
@@ -36522,6 +36524,10 @@ BEGIN |
36522 | 36524 | THEN N'exclude system databases (master, model, msdb, tempdb)' |
36523 | 36525 | WHEN N'@impact_threshold' |
36524 | 36526 | THEN N'minimum impact_score to surface in results' |
| 36527 | + WHEN N'@find_single_use_plans' |
| 36528 | + THEN N'show single-use plans consuming the most memory' |
| 36529 | + WHEN N'@find_duplicate_plans' |
| 36530 | + THEN N'show query hashes with multiple cached plans' |
36525 | 36531 | WHEN N'@debug' |
36526 | 36532 | THEN N'print diagnostic information' |
36527 | 36533 | WHEN N'@help' |
@@ -36550,6 +36556,10 @@ BEGIN |
36550 | 36556 | THEN N'0 or 1' |
36551 | 36557 | WHEN N'@impact_threshold' |
36552 | 36558 | THEN N'0.00 to 1.00' |
| 36559 | + WHEN N'@find_single_use_plans' |
| 36560 | + THEN N'0 or 1' |
| 36561 | + WHEN N'@find_duplicate_plans' |
| 36562 | + THEN N'0 or 1' |
36553 | 36563 | WHEN N'@debug' |
36554 | 36564 | THEN N'0 or 1' |
36555 | 36565 | WHEN N'@help' |
@@ -36578,6 +36588,10 @@ BEGIN |
36578 | 36588 | THEN N'1' |
36579 | 36589 | WHEN N'@impact_threshold' |
36580 | 36590 | THEN N'0.50' |
| 36591 | + WHEN N'@find_single_use_plans' |
| 36592 | + THEN N'0' |
| 36593 | + WHEN N'@find_duplicate_plans' |
| 36594 | + THEN N'0' |
36581 | 36595 | WHEN N'@debug' |
36582 | 36596 | THEN N'0' |
36583 | 36597 | WHEN N'@help' |
@@ -36726,6 +36740,134 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
36726 | 36740 | RETURN; |
36727 | 36741 | END; |
36728 | 36742 |
|
| 36743 | + /* |
| 36744 | + ╔══════════════════════════════════════════════════╗ |
| 36745 | + ║ Single-use plans mode ║ |
| 36746 | + ╚══════════════════════════════════════════════════╝ |
| 36747 | + |
| 36748 | + Shows the largest single-use plans by cached size, |
| 36749 | + sorted by memory consumption descending. |
| 36750 | + */ |
| 36751 | + IF @find_single_use_plans = 1 |
| 36752 | + BEGIN |
| 36753 | + SELECT TOP (@top) |
| 36754 | + database_name = |
| 36755 | + DB_NAME(CONVERT(integer, pa.value)), |
| 36756 | + cached_plan_size_kb = |
| 36757 | + cp.size_in_bytes / 1024, |
| 36758 | + query_text = |
| 36759 | + st.text, |
| 36760 | + query_plan = |
| 36761 | + CASE |
| 36762 | + WHEN TRY_CAST(qp.query_plan AS xml) IS NOT NULL |
| 36763 | + THEN TRY_CAST(qp.query_plan AS xml) |
| 36764 | + WHEN TRY_CAST(qp.query_plan AS xml) IS NULL |
| 36765 | + THEN |
| 36766 | + ( |
| 36767 | + SELECT |
| 36768 | + [processing-instruction(query_plan)] = |
| 36769 | + N'-- ' + NCHAR(13) + NCHAR(10) + |
| 36770 | + N'-- This is a huge query plan.' + NCHAR(13) + NCHAR(10) + |
| 36771 | + N'-- Remove the headers and footers, save it as a .sqlplan file, and re-open it.' + NCHAR(13) + NCHAR(10) + |
| 36772 | + NCHAR(13) + NCHAR(10) + |
| 36773 | + REPLACE(qp.query_plan, N'<RelOp', NCHAR(13) + NCHAR(10) + N'<RelOp') + |
| 36774 | + NCHAR(13) + NCHAR(10) COLLATE Latin1_General_Bin2 |
| 36775 | + FOR |
| 36776 | + XML |
| 36777 | + PATH(N''), |
| 36778 | + TYPE |
| 36779 | + ) |
| 36780 | + END, |
| 36781 | + qs.query_hash, |
| 36782 | + qs.query_plan_hash, |
| 36783 | + qs.creation_time, |
| 36784 | + qs.last_execution_time, |
| 36785 | + qs.sql_handle, |
| 36786 | + qs.plan_handle |
| 36787 | + FROM sys.dm_exec_query_stats AS qs |
| 36788 | + JOIN sys.dm_exec_cached_plans AS cp |
| 36789 | + ON cp.plan_handle = qs.plan_handle |
| 36790 | + CROSS APPLY |
| 36791 | + ( |
| 36792 | + SELECT TOP (1) |
| 36793 | + value = pa.value |
| 36794 | + FROM sys.dm_exec_plan_attributes(qs.plan_handle) AS pa |
| 36795 | + WHERE pa.attribute = N'dbid' |
| 36796 | + ) AS pa |
| 36797 | + CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st |
| 36798 | + OUTER APPLY sys.dm_exec_text_query_plan |
| 36799 | + ( |
| 36800 | + qs.plan_handle, |
| 36801 | + qs.statement_start_offset, |
| 36802 | + qs.statement_end_offset |
| 36803 | + ) AS qp |
| 36804 | + WHERE qs.execution_count = 1 |
| 36805 | + AND (@ignore_system_databases = 0 OR ISNULL(CONVERT(integer, pa.value), 0) NOT IN (1, 2, 3, 4)) |
| 36806 | + AND ISNULL(CONVERT(integer, pa.value), 0) < 32761 |
| 36807 | + AND (@database_id IS NULL OR CONVERT(integer, pa.value) = @database_id) |
| 36808 | + ORDER BY |
| 36809 | + cp.size_in_bytes DESC |
| 36810 | + OPTION(RECOMPILE, MAXDOP 1); |
| 36811 | + |
| 36812 | + RETURN; |
| 36813 | + END; |
| 36814 | + |
| 36815 | + /* |
| 36816 | + ╔══════════════════════════════════════════════════╗ |
| 36817 | + ║ Duplicate plans mode ║ |
| 36818 | + ╚══════════════════════════════════════════════════╝ |
| 36819 | + |
| 36820 | + Shows query hashes that have been compiled into |
| 36821 | + multiple cached plans, sorted by plan count descending. |
| 36822 | + */ |
| 36823 | + IF @find_duplicate_plans = 1 |
| 36824 | + BEGIN |
| 36825 | + SELECT TOP (@top) |
| 36826 | + database_name = |
| 36827 | + DB_NAME(CONVERT(integer, MAX(pa.value))), |
| 36828 | + qs.query_hash, |
| 36829 | + plan_count = |
| 36830 | + COUNT_BIG(DISTINCT qs.plan_handle), |
| 36831 | + total_executions = |
| 36832 | + SUM(qs.execution_count), |
| 36833 | + total_cpu_ms = |
| 36834 | + SUM(qs.total_worker_time) / 1000.0, |
| 36835 | + total_logical_reads = |
| 36836 | + SUM(qs.total_logical_reads), |
| 36837 | + total_cached_size_kb = |
| 36838 | + SUM(cp.size_in_bytes) / 1024, |
| 36839 | + oldest_plan = |
| 36840 | + MIN(qs.creation_time), |
| 36841 | + newest_plan = |
| 36842 | + MAX(qs.creation_time), |
| 36843 | + sample_query_text = |
| 36844 | + MAX(st.text) |
| 36845 | + FROM sys.dm_exec_query_stats AS qs |
| 36846 | + JOIN sys.dm_exec_cached_plans AS cp |
| 36847 | + ON cp.plan_handle = qs.plan_handle |
| 36848 | + CROSS APPLY |
| 36849 | + ( |
| 36850 | + SELECT TOP (1) |
| 36851 | + value = pa.value |
| 36852 | + FROM sys.dm_exec_plan_attributes(qs.plan_handle) AS pa |
| 36853 | + WHERE pa.attribute = N'dbid' |
| 36854 | + ) AS pa |
| 36855 | + CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st |
| 36856 | + WHERE qs.query_hash <> 0x0000000000000000 |
| 36857 | + AND (@ignore_system_databases = 0 OR ISNULL(CONVERT(integer, pa.value), 0) NOT IN (1, 2, 3, 4)) |
| 36858 | + AND ISNULL(CONVERT(integer, pa.value), 0) < 32761 |
| 36859 | + AND (@database_id IS NULL OR CONVERT(integer, pa.value) = @database_id) |
| 36860 | + GROUP BY |
| 36861 | + qs.query_hash |
| 36862 | + HAVING |
| 36863 | + COUNT_BIG(DISTINCT qs.plan_handle) > 1 |
| 36864 | + ORDER BY |
| 36865 | + COUNT_BIG(DISTINCT qs.plan_handle) DESC |
| 36866 | + OPTION(RECOMPILE, MAXDOP 1); |
| 36867 | + |
| 36868 | + RETURN; |
| 36869 | + END; |
| 36870 | + |
36729 | 36871 | /* |
36730 | 36872 | ╔══════════════════════════════════════════════════╗ |
36731 | 36873 | ║ Plan cache health analysis ║ |
@@ -36937,7 +37079,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
36937 | 37079 | WHERE pa.attribute = N'dbid' |
36938 | 37080 | ) AS pa |
36939 | 37081 | WHERE pa.value IS NOT NULL |
36940 | | - AND CONVERT(integer, pa.value) NOT IN (1, 2, 3, 4) |
| 37082 | + AND (@ignore_system_databases = 0 OR CONVERT(integer, pa.value) NOT IN (1, 2, 3, 4)) |
36941 | 37083 | AND CONVERT(integer, pa.value) < 32761 |
36942 | 37084 | AND (@database_id IS NULL OR CONVERT(integer, pa.value) = @database_id) |
36943 | 37085 | GROUP BY |
@@ -37046,7 +37188,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
37046 | 37188 | WHERE pa.attribute = N'dbid' |
37047 | 37189 | ) AS pa |
37048 | 37190 | WHERE pa.value IS NOT NULL |
37049 | | - AND CONVERT(integer, pa.value) NOT IN (1, 2, 3, 4) |
| 37191 | + AND (@ignore_system_databases = 0 OR CONVERT(integer, pa.value) NOT IN (1, 2, 3, 4)) |
37050 | 37192 | AND CONVERT(integer, pa.value) < 32761 |
37051 | 37193 | AND (@database_id IS NULL OR CONVERT(integer, pa.value) = @database_id) |
37052 | 37194 | GROUP BY |
|
0 commit comments