Skip to content

Commit d759d9b

Browse files
dedupe_only mode
trying to add a dedupe_only mode to index cleanup, if someone wants to not trigger a bunch of unused index disable commands and just dedupe existing indexes. this makes sense for servers that aren't up for a long time, and for people who (rightfully) may not trust usage details.
1 parent c006cc8 commit d759d9b

2 files changed

Lines changed: 61 additions & 27 deletions

File tree

sp_IndexCleanup/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ The procedure requires SQL Server 2012 (11.0) or later due to the use of FORMAT
2929
| @min_writes | bigint | 0 | Minimum number of writes for an index to be considered used |
3030
| @min_size_gb | decimal(10,2) | 0 | Minimum size in GB for an index to be analyzed |
3131
| @min_rows | bigint | 0 | Minimum number of rows for a table to be analyzed |
32+
| @dedupe_only | bit | 0 | When set to 1, only performs index deduplication but does not mark unused indexes for removal |
3233
| @get_all_databases | bit | 0 | When set to 1, analyzes all eligible databases on the server |
3334
| @include_databases | nvarchar(max) | NULL | Comma-separated list of databases to include (used with @get_all_databases = 1) |
3435
| @exclude_databases | nvarchar(max) | NULL | Comma-separated list of databases to exclude (used with @get_all_databases = 1) |
@@ -50,6 +51,11 @@ EXECUTE dbo.sp_IndexCleanup
5051
@table_name = 'YourTable',
5152
@debug = 1;
5253

54+
-- Only perform deduplication without marking unused indexes for removal
55+
EXECUTE dbo.sp_IndexCleanup
56+
@database_name = 'YourDatabase',
57+
@dedupe_only = 1;
58+
5359
-- Filter indexes by minimum usage thresholds
5460
EXECUTE dbo.sp_IndexCleanup
5561
@database_name = 'YourDatabase',
@@ -79,6 +85,7 @@ EXECUTE dbo.sp_IndexCleanup
7985
## Notes
8086

8187
- The procedure issues a warning when server uptime is less than 14 days, as index usage stats may not be representative
88+
- When server uptime is less than 7 days, @dedupe_only mode is automatically enabled to prevent removing unused indexes with insufficient usage data
8289
- Certain features like online index operations and compression are only available in specific SQL Server editions (Enterprise, Azure SQL DB, Managed Instance)
8390
- It is recommended to have a recent backup before making any index changes
8491
- The multi-database processing feature (@get_all_databases) analyzes each database sequentially for better performance and resource management

sp_IndexCleanup/sp_IndexCleanup.sql

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ ALTER PROCEDURE
2323
@min_writes bigint = 0,
2424
@min_size_gb decimal(10,2) = 0,
2525
@min_rows bigint = 0,
26+
@dedupe_only bit = 0, /*only perform deduplication, don't mark unused indexes for removal*/
2627
@get_all_databases bit = 0, /*looks for all accessible user databases and returns combined results*/
2728
@include_databases nvarchar(max) = NULL, /*comma-separated list of databases to include (only when @get_all_databases = 1)*/
2829
@exclude_databases nvarchar(max) = NULL, /*comma-separated list of databases to exclude (only when @get_all_databases = 1)*/
@@ -109,6 +110,7 @@ BEGIN TRY
109110
WHEN N'@min_writes' THEN 'minimum number of writes for an index to be considered used'
110111
WHEN N'@min_size_gb' THEN 'minimum size in GB for an index to be analyzed'
111112
WHEN N'@min_rows' THEN 'minimum number of rows for a table to be analyzed'
113+
WHEN N'@dedupe_only' THEN 'only perform index deduplication, do not mark unused indexes for removal'
112114
WHEN N'@get_all_databases' THEN 'set to 1 to analyze all accessible user databases'
113115
WHEN N'@include_databases' THEN 'comma-separated list of databases to include when @get_all_databases = 1'
114116
WHEN N'@exclude_databases' THEN 'comma-separated list of databases to exclude when @get_all_databases = 1'
@@ -128,6 +130,7 @@ BEGIN TRY
128130
WHEN N'@min_writes' THEN 'any positive integer or 0'
129131
WHEN N'@min_size_gb' THEN 'any positive decimal or 0'
130132
WHEN N'@min_rows' THEN 'any positive integer or 0'
133+
WHEN N'@dedupe_only' THEN '0 or 1 - only perform index deduplication, do not mark unused indexes for removal'
131134
WHEN N'@get_all_databases' THEN '0 or 1'
132135
WHEN N'@include_databases' THEN 'comma-separated list of database names'
133136
WHEN N'@exclude_databases' THEN 'comma-separated list of database names'
@@ -147,6 +150,7 @@ BEGIN TRY
147150
WHEN N'@min_writes' THEN '0'
148151
WHEN N'@min_size_gb' THEN '0'
149152
WHEN N'@min_rows' THEN '0'
153+
WHEN N'@dedupe_only' THEN '0'
150154
WHEN N'@get_all_databases' THEN '0'
151155
WHEN N'@include_databases' THEN 'NULL'
152156
WHEN N'@exclude_databases' THEN 'NULL'
@@ -279,6 +283,17 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
279283
THEN 1
280284
ELSE 0
281285
END;
286+
287+
/* Auto-enable dedupe_only mode if server uptime is low */
288+
IF CONVERT(integer, @uptime_days) < 7 AND @dedupe_only = 0
289+
BEGIN
290+
IF @debug = 1
291+
BEGIN
292+
RAISERROR('Server uptime is less than 7 days. Automatically enabling @dedupe_only mode.', 0, 1) WITH NOWAIT;
293+
END;
294+
295+
SELECT @dedupe_only = 1;
296+
END;
282297

283298
/*
284299
Initial checks for object validity
@@ -2249,33 +2264,36 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22492264
END;
22502265

22512266
/* Rule 1: Identify unused indexes */
2252-
UPDATE
2253-
#index_analysis
2254-
SET
2255-
#index_analysis.consolidation_rule =
2256-
CASE
2257-
WHEN @uptime_warning = 1
2258-
THEN 'Unused Index (WARNING: Server uptime < 14 days - usage data may be incomplete)'
2259-
ELSE 'Unused Index'
2260-
END,
2261-
#index_analysis.action = N'DISABLE'
2262-
WHERE EXISTS
2263-
(
2264-
SELECT
2265-
1/0
2266-
FROM #index_details id
2267-
WHERE id.database_id = #index_analysis.database_id
2268-
AND id.object_id = #index_analysis.object_id
2269-
AND id.index_id = #index_analysis.index_id
2270-
AND id.user_seeks = 0
2271-
AND id.user_scans = 0
2272-
AND id.user_lookups = 0
2273-
AND id.is_primary_key = 0 /* Don't disable primary keys */
2274-
AND id.is_unique_constraint = 0 /* Don't disable unique constraints */
2275-
AND id.is_eligible_for_dedupe = 1 /* Only eligible indexes */
2276-
)
2277-
AND #index_analysis.index_id <> 1
2278-
OPTION(RECOMPILE); /* Don't disable clustered indexes */
2267+
IF @dedupe_only = 0
2268+
BEGIN
2269+
UPDATE
2270+
#index_analysis
2271+
SET
2272+
#index_analysis.consolidation_rule =
2273+
CASE
2274+
WHEN @uptime_warning = 1
2275+
THEN 'Unused Index (WARNING: Server uptime < 14 days - usage data may be incomplete)'
2276+
ELSE 'Unused Index'
2277+
END,
2278+
#index_analysis.action = N'DISABLE'
2279+
WHERE EXISTS
2280+
(
2281+
SELECT
2282+
1/0
2283+
FROM #index_details id
2284+
WHERE id.database_id = #index_analysis.database_id
2285+
AND id.object_id = #index_analysis.object_id
2286+
AND id.index_id = #index_analysis.index_id
2287+
AND id.user_seeks = 0
2288+
AND id.user_scans = 0
2289+
AND id.user_lookups = 0
2290+
AND id.is_primary_key = 0 /* Don't disable primary keys */
2291+
AND id.is_unique_constraint = 0 /* Don't disable unique constraints */
2292+
AND id.is_eligible_for_dedupe = 1 /* Only eligible indexes */
2293+
)
2294+
AND #index_analysis.index_id <> 1
2295+
OPTION(RECOMPILE); /* Don't disable clustered indexes */
2296+
END;
22792297

22802298
IF @debug = 1
22812299
BEGIN
@@ -5760,6 +5778,15 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
57605778
irs.schema_name,
57615779
irs.table_name
57625780
OPTION(RECOMPILE);
5781+
5782+
/* Output message for dedupe_only mode */
5783+
IF @dedupe_only = 1
5784+
BEGIN
5785+
IF @debug = 1
5786+
BEGIN
5787+
RAISERROR('Note: Operating in dedupe_only mode. Unused indexes were considered for deduplication only, not for removal.', 0, 1) WITH NOWAIT;
5788+
END;
5789+
END;
57635790

57645791
END TRY
57655792
BEGIN CATCH

0 commit comments

Comments
 (0)