Skip to content

sp_IndexCleanup: base the unused-index window on each database's restore or create time, not only server uptime #907

Closed

Description

@erikdarlingdata

Which script is affected?

sp_IndexCleanup

Script Version

2.10 (20261001), main and dev at commit 99aed5a

SQL Server Version

SQL Server 2022 CU25 (16.0.4255.1)

Describe the Bug

The low-uptime guard reads the server's start time from sys.dm_os_sys_info. At 7 days or less it switches to @dedupe_only, and under 14 days it adds a warning to each Unused Index row.

Index usage also starts over when a database is restored or created, but the guard does not look at that. On a test server that had been up for weeks, with a database restored a week earlier, the proc reported 380 to 501 indexes per database as unused with no warning. On an AG primary, reads on the readable secondaries are not counted either, and nothing says so.

Steps to Reproduce

  1. Use a server that has been up for more than 14 days.
  2. Restore a database, or create one, and use it only lightly.
  3. Run EXECUTE dbo.sp_IndexCleanup @database_name = N'<that database>'; in default mode.

Expected Behavior

The output shows that this database's usage only covers the days since its restore, warns on its Unused rows, and skips the unused rule if that is 7 days or less.

Actual Behavior

Every index the light use did not touch is an Unused Index row with a DISABLE script, and there is no warning, because the server uptime is over 14 days.

Additional Context

Suggested change. The display matters most with @get_all_databases = 1, so this keeps the information to one place per database:

  1. Add two columns to the existing DATABASE level row of the summary: usage_since and usage_since_reason. The value is the latest of the server start, the database's last restore (msdb.dbo.restorehistory) and its create_date. The reason names which one it was. Table rows leave both blank.
  2. Base the existing "Unused Index (WARNING: ...)" label on that database's own window instead of server uptime. When a database has been around as long as the server has been up, no row changes.
  3. Make the automatic switch to dedupe-only per database. A database restored 3 days ago skips the unused rule, and its DATABASE row says so. Other databases in the same run are judged normally.
  4. On an AG primary with readable secondaries, add a note to the DATABASE row that reads on the secondaries are not counted.

Keep the server uptime in the header row and the ANALYZED OBJECT DETAILS row, where it is now.

Activity

  1. added a commit that references this issue on Oct 24, 2026
  2. erikdarlingdata commented on Oct 24, 2026

    @erikdarlingdata
    OwnerAuthor

    Claude posting for Erik Darling.

    Fixed in #925, merged to dev as cfc0bcb. Each database now has its own usage window: the latest of server start, its last restore (msdb.dbo.restorehistory) and its create_date.

    • The DATABASE row shows usage_since and usage_since_reason.
    • The Unused Index warning follows that window. A database whose window is the server start gets the same label as before.
    • A database with 7 days of usage data or less skips the unused index rule, and its DATABASE row says so. Other databases in the same run are judged normally.
    • On an AG primary with readable secondaries, the reason notes that reads on the secondaries are not counted. This path has not been run: no test server has an AG.
    • Per-day rates on TABLE rows divide by the database window.
      The README update for the uptime guard ships with the release. The text is in the PR description.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsp_IndexCleanupReview unused and duplicative indexes

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions