Skip to content

sp_IndexCleanup: fix NULL database_name on Azure SQL DB - #840

Merged
erikdarlingdata merged 1 commit into
devfrom
fix/indexcleanup-azure-database-id
Jul 21, 2026
Merged

sp_IndexCleanup: fix NULL database_name on Azure SQL DB#840
erikdarlingdata merged 1 commit into
devfrom
fix/indexcleanup-azure-database-id

Conversation

@erikdarlingdata

Copy link
Copy Markdown
Owner

Summary

  • On Azure SQL DB, sys.databases.database_id is logical-server-scoped while DB_ID()/DB_NAME() and every DMV database_id column are database/elastic-pool-scoped, and the two id spaces need not agree. sp_IndexCleanup carried the sys.databases id and round-tripped it through DB_NAME(), which returns NULL for a foreign-space id — Msg 515 on the NOT NULL database_name columns of the analysis temp tables on Hyperscale.
  • Same mismatch silently emptied #operational_stats: fo.database_id = os.database_id compared server-space to DMV local-space ids.
  • Fix is direction-proof: ids are resolved locally, and names are never derived from ids.

Changes

  • sp_IndexCleanup/sp_IndexCleanup.sql
    • #databases (single- and multi-database inserts) stores ISNULL(DB_ID(d.name), d.database_id) — the local-space id, identical values on box product — aligning dm_db_index_operational_stats, dm_db_index_usage_stats, and the operational-stats join predicates within one id space.
    • Every DB_NAME(@database_id) / DB_NAME(os.database_id) / QUOTENAME(DB_NAME(@current_database_id)) site (#filtered_objects, #operational_stats, #index_details, #partition_stats, #index_analysis inserts, generated DDL) now uses @current_database_name, parameterized into the dynamic SQL.

Test Plan

  • Compiles clean on SQL 2017, 2022, 2025
  • Local harness on SQL2022: adversarial 32/32, fixture-cases 31/31 (executes all generated scripts), rule-coverage 40/40 (covers @get_all_databases multi-db path), no-access 4/4
  • Re-run on the Azure Hyperscale elastic pool database that reproduced the Msg 515 errors

Closes #839

Generated with Claude Code

On Azure SQL DB, sys.databases.database_id is scoped to the logical
server while DB_ID(), DB_NAME(), and the database_id column in every
DMV are scoped to the database/elastic pool, and the two id spaces do
not have to agree (documented in the DB_ID() remarks). The procedure
stored the sys.databases id in #databases, so on Hyperscale
DB_NAME(@database_id) resolved to NULL and the first insert into a
NOT NULL database_name column died with Msg 515. The same mismatch
made fo.database_id = os.database_id match nothing, silently emptying
#operational_stats.

Fix, direction-proof on both counts:
- #databases now stores ISNULL(DB_ID(d.name), d.database_id) - the
  local-space id, identical on box product - so every DMV predicate
  (dm_db_index_operational_stats, dm_db_index_usage_stats, the
  operational stats join) compares within one id space.
- Names are never derived from ids anymore: every
  DB_NAME(@database_id) / DB_NAME(os.database_id) /
  QUOTENAME(DB_NAME(@current_database_id)) site now uses
  @current_database_name, parameterized into the dynamic SQL.

Verified: compiles on SQL 2017/2022/2025; full local harness on 2022
passes (adversarial 32/32, fixture-cases 31/31 including the execute
check, rule-coverage 40/40 covering @get_all_databases, no-access 4/4).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@erikdarlingdata
erikdarlingdata merged commit 9ecd6c3 into dev Jul 21, 2026
5 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/indexcleanup-azure-database-id branch July 21, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant