diff --git a/Install-All/DarlingData.sql b/Install-All/DarlingData.sql index f1e17881..1bc7ceb0 100644 --- a/Install-All/DarlingData.sql +++ b/Install-All/DarlingData.sql @@ -1,4 +1,4 @@ --- Compile Date: 03/24/2025 13:48:58 UTC +-- Compile Date: 03/26/2025 03:15:19 UTC SET ANSI_NULLS ON; SET ANSI_PADDING ON; SET ANSI_WARNINGS ON; @@ -10807,12 +10807,12 @@ BEGIN lock_mode nvarchar(10) NULL, resource_owner_type nvarchar(256) NULL, transaction_count int NULL, - transaction_name nvarchar(512) NULL, + transaction_name nvarchar(1024) NULL, last_transaction_started datetime2(7) NULL, last_transaction_completed datetime2(7) NULL, client_option_1 varchar(261) NULL, client_option_2 varchar(307) NULL, - wait_resource nvarchar(100) NULL, + wait_resource nvarchar(1024) NULL, priority int NULL, log_used bigint NULL, client_app nvarchar(256) NULL, @@ -11281,7 +11281,7 @@ BEGIN wait_time = bd.value('(process/@waittime)[1]', 'bigint'), lastbatchstarted = bd.value('(process/@lastbatchstarted)[1]', 'datetime2'), lastbatchcompleted = bd.value('(process/@lastbatchcompleted)[1]', 'datetime2'), - wait_resource = bd.value('(process/@waitresource)[1]', 'nvarchar(100)'), + wait_resource = bd.value('(process/@waitresource)[1]', 'nvarchar(1024)'), status = bd.value('(process/@status)[1]', 'nvarchar(10)'), priority = bd.value('(process/@priority)[1]', 'integer'), transaction_count = bd.value('(process/@trancount)[1]', 'integer'), @@ -11338,7 +11338,7 @@ BEGIN wait_time = bg.value('(process/@waittime)[1]', 'bigint'), last_transaction_started = bg.value('(process/@lastbatchstarted)[1]', 'datetime2'), last_transaction_completed = bg.value('(process/@lastbatchcompleted)[1]', 'datetime2'), - wait_resource = bg.value('(process/@waitresource)[1]', 'nvarchar(100)'), + wait_resource = bg.value('(process/@waitresource)[1]', 'nvarchar(1024)'), status = bg.value('(process/@status)[1]', 'nvarchar(10)'), priority = bg.value('(process/@priority)[1]', 'integer'), transaction_count = bg.value('(process/@trancount)[1]', 'integer'), @@ -11890,10 +11890,10 @@ SELECT blocked_ecid = bd.value('(process/@ecid)[1]', 'integer'), query_text_pre = bd.value('(process/inputbuf/text())[1]', 'nvarchar(max)'), wait_time = bd.value('(process/@waittime)[1]', 'bigint'), - transaction_name = bd.value('(process/@transactionname)[1]', 'nvarchar(512)'), + transaction_name = bd.value('(process/@transactionname)[1]', 'nvarchar(1024)'), last_transaction_started = bd.value('(process/@lasttranstarted)[1]', 'datetime2'), last_transaction_completed = CONVERT(datetime2, NULL), - wait_resource = bd.value('(process/@waitresource)[1]', 'nvarchar(100)'), + wait_resource = bd.value('(process/@waitresource)[1]', 'nvarchar(1024)'), lock_mode = bd.value('(process/@lockMode)[1]', 'nvarchar(10)'), status = bd.value('(process/@status)[1]', 'nvarchar(10)'), priority = bd.value('(process/@priority)[1]', 'integer'), @@ -12010,10 +12010,10 @@ SELECT blocked_ecid = bd.value('(process/@ecid)[1]', 'integer'), query_text_pre = bg.value('(process/inputbuf/text())[1]', 'nvarchar(max)'), wait_time = bg.value('(process/@waittime)[1]', 'bigint'), - transaction_name = bg.value('(process/@transactionname)[1]', 'nvarchar(512)'), + transaction_name = bg.value('(process/@transactionname)[1]', 'nvarchar(1024)'), last_transaction_started = bg.value('(process/@lastbatchstarted)[1]', 'datetime2'), last_transaction_completed = bg.value('(process/@lastbatchcompleted)[1]', 'datetime2'), - wait_resource = bg.value('(process/@waitresource)[1]', 'nvarchar(100)'), + wait_resource = bg.value('(process/@waitresource)[1]', 'nvarchar(1024)'), lock_mode = bg.value('(process/@lockMode)[1]', 'nvarchar(10)'), status = bg.value('(process/@status)[1]', 'nvarchar(10)'), priority = bg.value('(process/@priority)[1]', 'integer'), @@ -13720,28 +13720,30 @@ GO ALTER PROCEDURE dbo.sp_IndexCleanup ( - @database_name sysname = NULL, - @schema_name sysname = NULL, - @table_name sysname = NULL, - @min_reads bigint = 0, - @min_writes bigint = 0, - @min_size_gb decimal(10,2) = 0, - @min_rows bigint = 0, - @get_all_databases bit = 0, /*looks for all accessible user databases and returns combined results*/ + @database_name sysname = NULL, /*focus on a single database*/ + @schema_name sysname = NULL, /*use when focusing on a single table, or to a single schema with no table name*/ + @table_name sysname = NULL, /*use when focusing on a single table*/ + @min_reads bigint = 0, /*only look at indexes with a minimum number of reads*/ + @min_writes bigint = 0, /*only look at indexes with a minimum number of writes*/ + @min_size_gb decimal(10,2) = 0, /*only look at indexes with a minimum size*/ + @min_rows bigint = 0, /*only look at indexes with a minimum number of rows*/ + @dedupe_only bit = 'false', /*only perform deduplication, don't mark unused indexes for removal*/ + @get_all_databases bit = 'false', /*looks for all accessible user databases and returns combined results*/ @include_databases nvarchar(max) = NULL, /*comma-separated list of databases to include (only when @get_all_databases = 1)*/ @exclude_databases nvarchar(max) = NULL, /*comma-separated list of databases to exclude (only when @get_all_databases = 1)*/ - @help bit = 'false', - @debug bit = 'false', - @version varchar(20) = NULL OUTPUT, - @version_date datetime = NULL OUTPUT + @help bit = 'false', /*learn about the procedure and parameters*/ + @debug bit = 'false', /*print dynamic sql, show temp table contents*/ + @version varchar(20) = NULL OUTPUT, /*script version number*/ + @version_date datetime = NULL OUTPUT /*script version date*/ ) WITH RECOMPILE AS BEGIN SET NOCOUNT ON; - BEGIN TRY - /* Check for SQL Server 2012 (11.0) or later for FORMAT and CONCAT functions*/ + SELECT + @version = '1.4', + @version_date = '20250401'; IF /* Check SQL Server 2012+ for FORMAT and CONCAT functions */ @@ -13770,10 +13772,6 @@ BEGIN TRY RETURN; END; - SELECT - @version = '1.4', - @version_date = '20250401'; - /* Help section, for help. Will become more helpful when out of beta. @@ -13793,7 +13791,10 @@ BEGIN TRY help = N'always validate all changes against a non-production environment!' UNION ALL SELECT - help = N'please test carefully.'; + help = N'please test carefully.' + UNION ALL + SELECT + help = N'brought to you by erikdarling.com / code.erikdarling.com'; /* Parameters @@ -13807,12 +13808,13 @@ BEGIN TRY CASE ap.name WHEN N'@database_name' THEN 'the name of the database you wish to analyze' - WHEN N'@schema_name' THEN 'the schema name to filter indexes by' - WHEN N'@table_name' THEN 'the table name to filter indexes by' + WHEN N'@schema_name' THEN 'limits analysis to tables in the specified schema when used without @table_name' + WHEN N'@table_name' THEN 'the table name to filter indexes by, requires @schema_name if not dbo' WHEN N'@min_reads' THEN 'minimum number of reads for an index to be considered used' WHEN N'@min_writes' THEN 'minimum number of writes for an index to be considered used' WHEN N'@min_size_gb' THEN 'minimum size in GB for an index to be analyzed' WHEN N'@min_rows' THEN 'minimum number of rows for a table to be analyzed' + WHEN N'@dedupe_only' THEN 'only perform index deduplication, do not mark unused indexes for removal' WHEN N'@get_all_databases' THEN 'set to 1 to analyze all accessible user databases' WHEN N'@include_databases' THEN 'comma-separated list of databases to include when @get_all_databases = 1' WHEN N'@exclude_databases' THEN 'comma-separated list of databases to exclude when @get_all_databases = 1' @@ -13832,6 +13834,7 @@ BEGIN TRY WHEN N'@min_writes' THEN 'any positive integer or 0' WHEN N'@min_size_gb' THEN 'any positive decimal or 0' WHEN N'@min_rows' THEN 'any positive integer or 0' + WHEN N'@dedupe_only' THEN '0 or 1 - only perform index deduplication, do not mark unused indexes for removal' WHEN N'@get_all_databases' THEN '0 or 1' WHEN N'@include_databases' THEN 'comma-separated list of database names' WHEN N'@exclude_databases' THEN 'comma-separated list of database names' @@ -13851,6 +13854,7 @@ BEGIN TRY WHEN N'@min_writes' THEN '0' WHEN N'@min_size_gb' THEN '0' WHEN N'@min_rows' THEN '0' + WHEN N'@dedupe_only' THEN '0' WHEN N'@get_all_databases' THEN '0' WHEN N'@include_databases' THEN 'NULL' WHEN N'@exclude_databases' THEN 'NULL' @@ -13984,12 +13988,23 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ELSE 0 END; + /* Auto-enable dedupe_only mode if server uptime is low */ + IF CONVERT(integer, @uptime_days) <= 7 AND @dedupe_only = 0 + BEGIN + IF @debug = 1 + BEGIN + RAISERROR('Server uptime is less than 7 days. Automatically enabling @dedupe_only mode.', 0, 1) WITH NOWAIT; + END; + + SET @dedupe_only = 1; + END; + /* Initial checks for object validity */ IF @debug = 1 BEGIN - RAISERROR('Checking paramaters...', 0, 0) WITH NOWAIT; + RAISERROR('Checking parameters...', 0, 0) WITH NOWAIT; END; IF @schema_name IS NULL @@ -14000,8 +14015,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. RAISERROR('Parameter @schema_name cannot be NULL when specifying a table, defaulting to dbo', 10, 1) WITH NOWAIT; END; - SELECT - @schema_name = N'dbo'; + SET @schema_name = N'dbo'; END; IF @min_reads < 0 @@ -14280,7 +14294,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CREATE TABLE #index_reporting_stats ( - summary_level varchar(20) NOT NULL, /* 'DATABASE', 'TABLE', 'INDEX', 'SUMMARY' */ + summary_level varchar(20) NOT NULL, /* 'DATABASE', 'TABLE', 'INDEX', 'SUMMARY' */ database_name sysname NULL, schema_name sysname NULL, table_name sysname NULL, @@ -14553,8 +14567,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'rdsadmin' ) BEGIN - SELECT - @database_name = DB_NAME(); + SET @database_name = DB_NAME(); END; /* Single database mode */ @@ -14581,7 +14594,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* Get the database_id for backwards compatibility */ SELECT @current_database_id = d.database_id - FROM #databases AS d; + FROM #databases AS d + OPTION(RECOMPILE); END; END ELSE @@ -14706,9 +14720,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. N'.' + QUOTENAME(@table_name); - SELECT - @object_id = - OBJECT_ID(@full_object_name); + SET @object_id = OBJECT_ID(@full_object_name); IF @object_id IS NULL BEGIN @@ -14817,7 +14829,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. )'; END; - IF @object_id IS NOT NULL BEGIN IF @debug = 1 @@ -14825,10 +14836,22 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. RAISERROR('adding object_id filter', 0, 0) WITH NOWAIT; END; - SELECT @sql += N' + SET @sql += N' AND t.object_id = @object_id'; END; + IF @schema_name IS NOT NULL + AND @object_id IS NULL + BEGIN + IF @debug = 1 + BEGIN + RAISERROR('adding schema_name filter', 0, 0) WITH NOWAIT; + END; + + SET @sql += N' + AND s.name = @schema_name'; + END; + SET @sql += N' AND EXISTS ( @@ -14899,13 +14922,15 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @min_writes bigint, @min_size_gb decimal(10,2), @min_rows bigint, - @object_id integer', + @object_id integer, + @schema_name sysname', @current_database_id, @min_reads, @min_writes, @min_size_gb, @min_rows, - @object_id; + @object_id, + @schema_name; SET @rc = ROWCOUNT_BIG(); @@ -15935,13 +15960,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ( SELECT 1/0 - FROM #index_details AS id + FROM #index_details AS id WHERE id.index_id = #index_analysis.index_id AND id.object_id = #index_analysis.object_id AND id.user_scans > 0 ) THEN 100 ELSE 0 - END - OPTION(RECOMPILE); /* Indexes with scans get some priority */ + END /* Indexes with scans get some priority */ + OPTION(RECOMPILE); IF @debug = 1 BEGIN @@ -15953,33 +15978,36 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. END; /* Rule 1: Identify unused indexes */ - UPDATE - #index_analysis - SET - #index_analysis.consolidation_rule = - CASE - WHEN @uptime_warning = 1 - THEN 'Unused Index (WARNING: Server uptime < 14 days - usage data may be incomplete)' - ELSE 'Unused Index' - END, - #index_analysis.action = N'DISABLE' - WHERE EXISTS - ( - SELECT - 1/0 - FROM #index_details id - WHERE id.database_id = #index_analysis.database_id - AND id.object_id = #index_analysis.object_id - AND id.index_id = #index_analysis.index_id - AND id.user_seeks = 0 - AND id.user_scans = 0 - AND id.user_lookups = 0 - AND id.is_primary_key = 0 /* Don't disable primary keys */ - AND id.is_unique_constraint = 0 /* Don't disable unique constraints */ - AND id.is_eligible_for_dedupe = 1 /* Only eligible indexes */ - ) - AND #index_analysis.index_id <> 1 - OPTION(RECOMPILE); /* Don't disable clustered indexes */ + IF @dedupe_only = 0 + BEGIN + UPDATE + #index_analysis + SET + #index_analysis.consolidation_rule = + CASE + WHEN @uptime_warning = 1 + THEN 'Unused Index (WARNING: Server uptime < 14 days - usage data may be incomplete)' + ELSE 'Unused Index' + END, + #index_analysis.action = N'DISABLE' + WHERE EXISTS + ( + SELECT + 1/0 + FROM #index_details id + WHERE id.database_id = #index_analysis.database_id + AND id.object_id = #index_analysis.object_id + AND id.index_id = #index_analysis.index_id + AND id.user_seeks = 0 + AND id.user_scans = 0 + AND id.user_lookups = 0 + AND id.is_primary_key = 0 /* Don't disable primary keys */ + AND id.is_unique_constraint = 0 /* Don't disable unique constraints */ + AND id.is_eligible_for_dedupe = 1 /* Only eligible indexes */ + ) + AND #index_analysis.index_id <> 1 /* Don't disable clustered indexes */ + OPTION(RECOMPILE); + END; IF @debug = 1 BEGIN @@ -16044,7 +16072,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. AND id2_uc.index_id = ia2.index_id AND id2_uc.is_unique_constraint = 1 ) - AND EXISTS + AND EXISTS ( SELECT 1/0 @@ -16356,7 +16384,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CASE /* If both have includes, combine them without duplicates */ WHEN kss.superset_includes IS NOT NULL - AND kss.subset_includes IS NOT NULL + AND kss.subset_includes IS NOT NULL THEN /* Create combined includes using XML method that works with all SQL Server versions */ ( @@ -16432,7 +16460,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. UPDATE ia2 SET - ia2.superseded_by = N'Supersedes ' + ia1.index_name + ia2.superseded_by = + N'Supersedes ' + + ia1.index_name FROM #index_analysis AS ia1 JOIN #index_analysis AS ia2 ON ia1.database_id = ia2.database_id @@ -16468,7 +16498,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. END FROM #index_analysis AS ia1 WHERE ia1.consolidation_rule IS NULL /* Not already processed */ - AND ia1.action IS NULL /* Not already processed by earlier rules */ + AND ia1.action IS NULL /* Not already processed by earlier rules */ AND EXISTS ( /* Find nonclustered indexes */ @@ -17362,7 +17392,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* Sort duplicate/subset indexes first (20), then unused indexes last (25) */ sort_order = CASE - WHEN ia.consolidation_rule LIKE 'Unused Index%' THEN 25 + WHEN ia.consolidation_rule LIKE 'Unused Index%' + THEN 25 ELSE 20 END, ia.database_name, @@ -17645,16 +17676,20 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. OPTION(RECOMPILE); /* If any clustered indexes were added, mark them as KEEP */ - UPDATE #index_analysis - SET action = N'KEEP' - WHERE index_id = 1 /* Clustered indexes */ - AND action IS NULL; + UPDATE + #index_analysis + SET + #index_analysis.action = N'KEEP' + WHERE #index_analysis.index_id = 1 /* Clustered indexes */ + AND #index_analysis.action IS NULL; /* Update index priority for clustered indexes to ensure they're not chosen for deduplication */ - UPDATE #index_analysis - SET index_priority = 1000 /* Maximum priority */ - WHERE index_id = 1 /* Clustered indexes */ - AND index_priority IS NULL; + UPDATE + #index_analysis + SET + #index_analysis.index_priority = 1000 /* Maximum priority */ + WHERE #index_analysis.index_id = 1 /* Clustered indexes */ + AND #index_analysis.index_priority IS NULL; IF @debug = 1 BEGIN @@ -18147,7 +18182,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. script_type = CASE /* Add compression status to script_type */ - WHEN ce.can_compress = 1 THEN 'KEPT - NEEDS COMPRESSION' + WHEN ce.can_compress = 1 + THEN 'KEPT - NEEDS COMPRESSION' ELSE 'KEPT' END, ia.consolidation_rule, @@ -18535,7 +18571,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. total_min_savings_gb = ( SELECT - SUM( + SUM + ( CASE WHEN subia.action = N'DISABLE' THEN subps.total_space_gb @@ -18560,7 +18597,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. total_max_savings_gb = ( SELECT - SUM( + SUM + ( CASE WHEN subia.action = N'DISABLE' THEN subps.total_space_gb @@ -18783,7 +18821,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. total_min_savings_gb = ( SELECT - SUM( + SUM + ( CASE WHEN subia.action = N'DISABLE' THEN subps.total_space_gb @@ -18810,7 +18849,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. total_max_savings_gb = ( SELECT - SUM( + SUM + ( CASE WHEN subia.action = N'DISABLE' THEN subps.total_space_gb @@ -18988,13 +19028,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ROW_NUMBER() OVER ( PARTITION BY - database_name, - schema_name, - table_name, - index_name, + irs.database_name, + irs.schema_name, + irs.table_name, + irs.index_name, irs.script_type ORDER BY - result_type DESC /* Prefer non-NULL result types */ + irs.result_type DESC /* Prefer non-NULL result types */ ) AS rn FROM #index_cleanup_results AS irs ) AS ir @@ -19223,7 +19263,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WHEN ISNULL(irs.unused_indexes, 0) > 0 THEN FORMAT ( - CONVERT(decimal(38,2), + CONVERT + ( + decimal(38,2), ISNULL ( irs.user_updates / @@ -19295,7 +19337,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THEN FORMAT ( - CONVERT(decimal(38,2), + CONVERT + ( + decimal(38,2), ISNULL ( (irs.row_lock_wait_count + irs.page_lock_wait_count) / @@ -19381,7 +19425,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THEN FORMAT ( - CONVERT(decimal(38,2), + CONVERT + ( + decimal(38,2), ISNULL ( (irs.page_latch_wait_count + irs.page_io_latch_wait_count) / @@ -19465,6 +19511,15 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. irs.table_name OPTION(RECOMPILE); + /* Output message for dedupe_only mode */ + IF @dedupe_only = 1 + BEGIN + IF @debug = 1 + BEGIN + RAISERROR('Note: Operating in dedupe_only mode. Unused indexes were considered for deduplication only, not for removal.', 0, 1) WITH NOWAIT; + END; + END; + END TRY BEGIN CATCH THROW; @@ -26174,7 +26229,13 @@ DECLARE @requires_secondary_processing bit, @split_sql nvarchar(max), @error_msg nvarchar(2000), - @conflict_list nvarchar(max) = N''; + @conflict_list nvarchar(max) = N'', + @database_cursor CURSOR, + @filter_cursor CURSOR, + @dynamic_sql nvarchar(max) = N'', + @secondary_sql nvarchar(max) = N'', + @temp_target_table nvarchar(100), + @exist_or_not_exist nvarchar(20); /* In cases where we are escaping @query_text_search and @@ -26740,9 +26801,6 @@ BEGIN END; END; -DECLARE - @database_cursor CURSOR; - SET @database_cursor = CURSOR @@ -27019,7 +27077,8 @@ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;', ) AS ids CROSS APPLY ids.nodes(''x'') AS x (x) ) AS ids - OPTION(RECOMPILE);', + OPTION(RECOMPILE); + ', @string_split_strings = N' SELECT DISTINCT ids = @@ -27063,7 +27122,8 @@ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;', ) AS ids CROSS APPLY ids.nodes(''x'') AS x (x) ) AS ids - OPTION(RECOMPILE);', + OPTION(RECOMPILE); + ', @troubleshoot_insert = N' INSERT #troubleshoot_performance @@ -27078,7 +27138,8 @@ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;', @current_table, GETDATE() ) - OPTION(RECOMPILE);', + OPTION(RECOMPILE); + ', @troubleshoot_update = N' UPDATE tp @@ -27086,7 +27147,8 @@ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;', tp.end_time = GETDATE() FROM #troubleshoot_performance AS tp WHERE tp.current_table = @current_table - OPTION(RECOMPILE);', + OPTION(RECOMPILE); + ', @troubleshoot_info = N' SELECT ( @@ -27105,7 +27167,8 @@ SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;', PATH(N''''), TYPE ).query(''.[1]'') AS current_query - OPTION(RECOMPILE);', + OPTION(RECOMPILE); + ', @rc = 0, @em = @expert_mode, @fo = @format_output, @@ -28530,9 +28593,6 @@ OR @ignore_plan_hashes IS NOT NULL OR @ignore_sql_handles IS NOT NULL ) BEGIN - DECLARE - @filter_cursor CURSOR; - SET @filter_cursor = CURSOR LOCAL @@ -28586,11 +28646,13 @@ BEGIN /* Choose appropriate string split function based on data type */ IF @data_type = N'bigint' BEGIN - SELECT @split_sql = @string_split_ints; + SELECT + @split_sql = @string_split_ints; END ELSE BEGIN - SELECT @split_sql = @string_split_strings; + SELECT + @split_sql = @string_split_strings; END; /* Execute the initial insert with troubleshooting if enabled */ @@ -28598,14 +28660,14 @@ BEGIN BEGIN EXECUTE sys.sp_executesql @troubleshoot_insert, - N'@current_table nvarchar(100)', + N'@current_table nvarchar(100)', @current_table; SET STATISTICS XML ON; END; /* Execute the dynamic SQL to populate the temporary table */ - DECLARE @dynamic_sql nvarchar(max) = N' + SET @dynamic_sql = N' INSERT INTO ' + @temp_table + N' WITH @@ -28615,9 +28677,14 @@ BEGIN N') EXECUTE sys.sp_executesql @split_sql, - N''@ids nvarchar(4000)'', + N''@ids nvarchar(4000)'', @param_value;'; + IF @debug = 1 + BEGIN + PRINT @dynamic_sql; + END; + EXEC sys.sp_executesql @dynamic_sql, N'@split_sql nvarchar(max), @@ -28631,7 +28698,7 @@ BEGIN EXECUTE sys.sp_executesql @troubleshoot_update, - N'@current_table nvarchar(100)', + N'@current_table nvarchar(100)', @current_table; EXECUTE sys.sp_executesql @@ -28649,13 +28716,12 @@ BEGIN @current_table = 'inserting #include_plan_ids for ' + @param_name; /* Build appropriate SQL based on parameter type */ - DECLARE - @secondary_sql nvarchar(max) = N''; - IF @param_name = 'include_query_ids' OR @param_name = 'ignore_query_ids' BEGIN - SELECT @secondary_sql = N' + SET @secondary_sql = @isolation_level; + + SELECT @secondary_sql += N' SELECT DISTINCT qsp.plan_id FROM ' + @database_name_quoted + N'.sys.query_store_plan AS qsp @@ -28672,13 +28738,16 @@ BEGIN N'_query_ids AS iqi WHERE iqi.query_id = qsp.query_id ) - OPTION(RECOMPILE);'; + OPTION(RECOMPILE);' + @nc10; END; ELSE + IF @param_name = 'include_query_hashes' OR @param_name = 'ignore_query_hashes' BEGIN - SELECT @secondary_sql = N' + SET @secondary_sql = @isolation_level; + + SELECT @secondary_sql += N' SELECT DISTINCT qsp.plan_id FROM ' + @database_name_quoted + N'.sys.query_store_plan AS qsp @@ -28702,13 +28771,16 @@ BEGIN WHERE iqh.query_hash = qsq.query_hash ) ) - OPTION(RECOMPILE);'; + OPTION(RECOMPILE);' + @nc10; END; ELSE + IF @param_name = 'include_plan_hashes' OR @param_name = 'ignore_plan_hashes' BEGIN - SELECT @secondary_sql = N' + SET @secondary_sql = @isolation_level; + + SELECT @secondary_sql += N' SELECT DISTINCT qsp.plan_id FROM ' + @database_name_quoted + N'.sys.query_store_plan AS qsp @@ -28724,14 +28796,16 @@ BEGIN END + N'_plan_hashes AS iph WHERE iph.plan_hash = qsp.query_plan_hash ) - OPTION(RECOMPILE);'; + OPTION(RECOMPILE);' + @nc10; END; ELSE - IF - @param_name = 'include_sql_handles' + + IF @param_name = 'include_sql_handles' OR @param_name = 'ignore_sql_handles' BEGIN - SELECT @secondary_sql = N' + SET @secondary_sql = @isolation_level; + + SELECT @secondary_sql += N' SELECT DISTINCT qsp.plan_id FROM ' + @database_name_quoted + N'.sys.query_store_plan AS qsp @@ -28761,7 +28835,7 @@ BEGIN ) ) ) - OPTION(RECOMPILE);'; + OPTION(RECOMPILE);' + @nc10; END; /* Process secondary sql if defined */ @@ -28771,21 +28845,42 @@ BEGIN BEGIN EXECUTE sys.sp_executesql @troubleshoot_insert, - N'@current_table nvarchar(100)', + N'@current_table nvarchar(100)', @current_table; SET STATISTICS XML ON; END; - INSERT INTO - #include_plan_ids - WITH - (TABLOCK) - ( - plan_id - ) - EXECUTE sys.sp_executesql - @secondary_sql; + IF @debug = 1 + BEGIN + PRINT @secondary_sql; + END; + + /* Insert into the correct target table based on include/ignore */ + IF @is_include = 1 + BEGIN + INSERT INTO + #include_plan_ids + WITH + (TABLOCK) + ( + plan_id + ) + EXECUTE sys.sp_executesql + @secondary_sql; + END + ELSE + BEGIN + INSERT INTO + #ignore_plan_ids + WITH + (TABLOCK) + ( + plan_id + ) + EXECUTE sys.sp_executesql + @secondary_sql; + END; IF @troubleshoot_performance = 1 BEGIN @@ -28793,32 +28888,39 @@ BEGIN EXECUTE sys.sp_executesql @troubleshoot_update, - N'@current_table nvarchar(100)', + N'@current_table nvarchar(100)', @current_table; EXECUTE sys.sp_executesql @troubleshoot_info, - N'@sql nvarchar(max), @current_table nvarchar(100)', + N'@sql nvarchar(max), @current_table nvarchar(100)', @secondary_sql, @current_table; END; END; + END; - /* Update where clause if needed */ - DECLARE - @temp_target_table nvarchar(100) = + /* Update where clause based on parameter type */ + IF @param_name = 'include_plan_ids' + OR @param_name = 'ignore_plan_ids' + OR @requires_secondary_processing = 1 + BEGIN + /* Choose the correct table and exists/not exists operator */ + SELECT + @temp_target_table = CASE WHEN @is_include = 1 THEN N'#include_plan_ids' ELSE N'#ignore_plan_ids' END, - @exist_or_not_exist nvarchar(20) = + @exist_or_not_exist = CASE WHEN @is_include = 1 THEN N'EXISTS' ELSE N'NOT EXISTS' END; + /* Add the filter condition to the where clause */ SELECT @where_clause += N'AND ' + @@ -28830,6 +28932,11 @@ BEGIN FROM ' + @temp_target_table + N' AS idi WHERE idi.plan_id = qsrs.plan_id )' + @nc10; + + IF @debug = 1 + BEGIN + PRINT @where_clause; + END; END; FETCH NEXT @@ -32211,7 +32318,7 @@ WHERE EXISTS AND qsp.database_id = qsq.database_id WHERE qsq.context_settings_id = qcs.context_settings_id ) -OPTION(RECOMPILE);'; +OPTION(RECOMPILE);' + @nc10; INSERT #query_context_settings @@ -33156,8 +33263,7 @@ ORDER BY END END END + N' DESC -OPTION(RECOMPILE);' - + @nc10 +OPTION(RECOMPILE);' + @nc10 ); IF @debug = 1 @@ -33267,7 +33373,7 @@ BEGIN FROM #query_store_plan_feedback AS qspf ORDER BY qspf.plan_id - OPTION(RECOMPILE);'; + OPTION(RECOMPILE);' + @nc10; IF @debug = 1 BEGIN @@ -33325,7 +33431,7 @@ BEGIN FROM #query_store_query_hints AS qsqh ORDER BY qsqh.query_id - OPTION(RECOMPILE);'; + OPTION(RECOMPILE);' + @nc10; IF @debug = 1 BEGIN @@ -33373,7 +33479,7 @@ BEGIN FROM #query_store_query_variant AS qsqv ORDER BY qsqv.parent_query_id - OPTION(RECOMPILE);'; + OPTION(RECOMPILE);' + @nc10; IF @debug = 1 BEGIN @@ -33664,7 +33770,7 @@ BEGIN WHERE x.n = 1 ORDER BY x.query_id - OPTION(RECOMPILE);'; + OPTION(RECOMPILE);' + @nc10; IF @debug = 1 BEGIN @@ -33880,7 +33986,7 @@ BEGIN ) ORDER BY qsq.query_id - OPTION(RECOMPILE);' + OPTION(RECOMPILE);' + @nc10 ); IF @debug = 1 @@ -33925,7 +34031,7 @@ BEGIN ( nvarchar(max), N' - SELECT DISTINCT + SELECT source = ''query_store_wait_stats_by_query'', database_name = @@ -34039,7 +34145,7 @@ BEGIN ORDER BY qsws.plan_id, qsws.total_query_wait_time_ms DESC - OPTION(RECOMPILE);' + OPTION(RECOMPILE);' + @nc10 ); IF @debug = 1 @@ -34178,7 +34284,7 @@ BEGIN qsws.database_id ORDER BY SUM(qsws.total_query_wait_time_ms) DESC - OPTION(RECOMPILE);' + OPTION(RECOMPILE);' + @nc10 ); IF @debug = 1 @@ -34278,7 +34384,7 @@ BEGIN N' dqso.size_based_cleanup_mode_desc FROM #database_query_store_options AS dqso - OPTION(RECOMPILE);' + OPTION(RECOMPILE);' + @nc10 ); IF @debug = 1 diff --git a/sp_IndexCleanup/sp_IndexCleanup.sql b/sp_IndexCleanup/sp_IndexCleanup.sql index 6459345f..49d734eb 100644 --- a/sp_IndexCleanup/sp_IndexCleanup.sql +++ b/sp_IndexCleanup/sp_IndexCleanup.sql @@ -283,7 +283,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THEN 1 ELSE 0 END; - + /* Auto-enable dedupe_only mode if server uptime is low */ IF CONVERT(integer, @uptime_days) <= 7 AND @dedupe_only = 0 BEGIN @@ -291,7 +291,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. BEGIN RAISERROR('Server uptime is less than 7 days. Automatically enabling @dedupe_only mode.', 0, 1) WITH NOWAIT; END; - + SET @dedupe_only = 1; END; @@ -1135,8 +1135,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SET @sql += N' AND t.object_id = @object_id'; END; - - IF @schema_name IS NOT NULL + + IF @schema_name IS NOT NULL AND @object_id IS NULL BEGIN IF @debug = 1 @@ -1316,6 +1316,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. END; /* Check for sparse columns or incompatible data types */ + /* Check for sparse columns or incompatible data types */ IF @can_compress = 1 BEGIN IF @debug = 1 @@ -1331,9 +1332,45 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ce SET ce.can_compress = 0, - ce.reason = ''Table contains sparse columns or incompatible data types'' + ce.reason = ''Table contains sparse columns'' FROM #compression_eligibility AS ce WHERE EXISTS + ( + SELECT + 1/0 + FROM ' + QUOTENAME(@current_database_name) + N'.sys.columns AS c + WHERE c.object_id = ce.object_id + AND + ( + c.is_sparse = 1 + ) + ) + OPTION(RECOMPILE); + '; + + IF @debug = 1 + BEGIN + PRINT @sql; + END; + + EXECUTE sys.sp_executesql + @sql; + + SELECT + @sql = N' + SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; + + UPDATE + ce + SET + ce.can_compress = 0, + ce.reason = ''Index contains incompatible data types'' + FROM #compression_eligibility AS ce + JOIN sys.indexes AS i + ON i.object_id = ce.object_id AND i.index_id = ce.index_id + WHERE ce.can_compress = 1 + AND i.type = 1 + AND EXISTS ( SELECT 1/0 @@ -1343,8 +1380,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WHERE c.object_id = ce.object_id AND ( - c.is_sparse = 1 - OR t.name IN (N''text'', N''ntext'', N''image'') + t.name IN (N''text'', N''ntext'', N''image'') ) ) OPTION(RECOMPILE); @@ -2262,7 +2298,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. AND id.user_scans > 0 ) THEN 100 ELSE 0 END /* Indexes with scans get some priority */ - OPTION(RECOMPILE); + OPTION(RECOMPILE); IF @debug = 1 BEGIN @@ -2302,7 +2338,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. AND id.is_eligible_for_dedupe = 1 /* Only eligible indexes */ ) AND #index_analysis.index_id <> 1 /* Don't disable clustered indexes */ - OPTION(RECOMPILE); + OPTION(RECOMPILE); END; IF @debug = 1 @@ -2756,8 +2792,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. UPDATE ia2 SET - ia2.superseded_by = - N'Supersedes ' + + ia2.superseded_by = + N'Supersedes ' + ia1.index_name FROM #index_analysis AS ia1 JOIN #index_analysis AS ia2 @@ -3688,7 +3724,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* Sort duplicate/subset indexes first (20), then unused indexes last (25) */ sort_order = CASE - WHEN ia.consolidation_rule LIKE 'Unused Index%' + WHEN ia.consolidation_rule LIKE 'Unused Index%' THEN 25 ELSE 20 END, @@ -3972,17 +4008,17 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. OPTION(RECOMPILE); /* If any clustered indexes were added, mark them as KEEP */ - UPDATE + UPDATE #index_analysis - SET + SET #index_analysis.action = N'KEEP' WHERE #index_analysis.index_id = 1 /* Clustered indexes */ AND #index_analysis.action IS NULL; /* Update index priority for clustered indexes to ensure they're not chosen for deduplication */ - UPDATE + UPDATE #index_analysis - SET + SET #index_analysis.index_priority = 1000 /* Maximum priority */ WHERE #index_analysis.index_id = 1 /* Clustered indexes */ AND #index_analysis.index_priority IS NULL; @@ -4478,7 +4514,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. script_type = CASE /* Add compression status to script_type */ - WHEN ce.can_compress = 1 + WHEN ce.can_compress = 1 THEN 'KEPT - NEEDS COMPRESSION' ELSE 'KEPT' END, @@ -5806,7 +5842,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. irs.schema_name, irs.table_name OPTION(RECOMPILE); - + /* Output message for dedupe_only mode */ IF @dedupe_only = 1 BEGIN diff --git a/sp_QuickieStore/sp_QuickieStore.sql b/sp_QuickieStore/sp_QuickieStore.sql index 53f8dd19..4b4cc0c0 100644 --- a/sp_QuickieStore/sp_QuickieStore.sql +++ b/sp_QuickieStore/sp_QuickieStore.sql @@ -4102,12 +4102,12 @@ BEGIN /* Choose appropriate string split function based on data type */ IF @data_type = N'bigint' BEGIN - SELECT + SELECT @split_sql = @string_split_ints; END ELSE BEGIN - SELECT + SELECT @split_sql = @string_split_strings; END; @@ -4124,8 +4124,8 @@ BEGIN /* Execute the dynamic SQL to populate the temporary table */ SET @dynamic_sql = N' - INSERT INTO - ' + @temp_table + N' + INSERT INTO + ' + @temp_table + N' WITH (TABLOCK) ( @@ -4171,8 +4171,8 @@ BEGIN SELECT @current_table = 'inserting #include_plan_ids for ' + @param_name; - /* Build appropriate SQL based on parameter type */ - IF @param_name = 'include_query_ids' + /* Build appropriate SQL based on parameter type */ + IF @param_name = 'include_query_ids' OR @param_name = 'ignore_query_ids' BEGIN SET @secondary_sql = @isolation_level; @@ -4197,7 +4197,7 @@ BEGIN OPTION(RECOMPILE);' + @nc10; END; ELSE - + IF @param_name = 'include_query_hashes' OR @param_name = 'ignore_query_hashes' BEGIN @@ -4230,7 +4230,7 @@ BEGIN OPTION(RECOMPILE);' + @nc10; END; ELSE - + IF @param_name = 'include_plan_hashes' OR @param_name = 'ignore_plan_hashes' BEGIN @@ -4255,7 +4255,7 @@ BEGIN OPTION(RECOMPILE);' + @nc10; END; ELSE - + IF @param_name = 'include_sql_handles' OR @param_name = 'ignore_sql_handles' BEGIN @@ -4357,7 +4357,7 @@ BEGIN END; /* Update where clause based on parameter type */ - IF @param_name = 'include_plan_ids' + IF @param_name = 'include_plan_ids' OR @param_name = 'ignore_plan_ids' OR @requires_secondary_processing = 1 BEGIN