Repository navigation
sp_IndexCleanup: carry index options through rebuilds and skip no-op merges - #914
Merged
Merged
Conversation
…merges Carry IGNORE_DUP_KEY and STATISTICS_NORECOMPUTE through DROP_EXISTING scripts (#905), skip MERGE SCRIPT rows that change nothing (#906), unwind winners when the foreign key guard keeps a loser, and make rule_coverage_test independent of server uptime. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015SjCdz1y4HnJ5yNfFpN3Dv
… fixture Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015SjCdz1y4HnJ5yNfFpN3Dv
Assertion 1d checked that the MERGE SCRIPT for uix_uc_acd says CREATE UNIQUE. Its subset, ix_uc_ac, had no column the superset lacked, so the merge changed nothing, and since #906 a merge that changes nothing gets no MERGE SCRIPT. ix_uc_ac now includes col_e, so the merge adds a column, and 1d also checks that the script includes [col_e]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015SjCdz1y4HnJ5yNfFpN3Dv
erikdarlingdata
marked this pull request as ready for review
October 5, 2026 18:38
|
Claude finished @erikdarlingdata's task in 18s —— View job Review of PR #914I read the Verdict: no blocking issues.
Minor notes, not blocking
The change looks good to me. |
The no-op check before the MERGE SCRIPT compares the merged include list with one it builds itself. Every statement that writes included_columns uses the same format and column_name order today. Say so at Rule 6 and at the check, so a change to one builder does not quietly bring back MERGE SCRIPTs that change nothing. Comments only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015SjCdz1y4HnJ5yNfFpN3Dv
|
Claude finished @erikdarlingdata's task in 10s —— View job Re-review after latest push (89801f0)The new commit only adds comments to Verdict: no blocking issues.
Still open, and not blocking: the MAKE UNIQUE branch of the winner unwind has no test. The PR description already says so. |
This was referenced Oct 5, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #905
Fixes #906
Rebuild scripts keep IGNORE_DUP_KEY and STATISTICS_NORECOMPUTE (#905)
The procedure now reads two more settings for every index:
sys.indexes.ignore_dup_key, and theno_recomputeflag of the matchingsys.statsrow (stats_id = index_id). It collects them the same way PR #913 added the foreign key column.The MERGE SCRIPT and the filtered-index include script add
IGNORE_DUP_KEY = ONandSTATISTICS_NORECOMPUTE = ONwhen the index being rebuilt has them. A MAKE UNIQUE script also takesIGNORE_DUP_KEY = ONfrom the unique constraint that it replaces. So inserts that SQL Server skipped before the constraint is dropped are still skipped after. Both columns exist on every supported version, so the change needs no version check.The dynamic SQL that fills
#index_detailswas close to 4,000 characters. At that length, a string with no max-type operand is cut off with no error. The string now starts from an emptynvarchar(max), so the new columns fit. A comment above it gives the reason.No MERGE SCRIPT when the merge changes nothing (#906)
The old check asked whether Rule 6 had recorded merged includes. But Rule 6 records a row for every Key Superset winner, whether or not the include list changes. The check also looked only at MERGE INCLUDES rows. So it missed both cases in the issue.
The new check compares the winner's final include list with its own includes, rebuilt from
#index_detailsin the same order. For MAKE UNIQUE rows, it also checks uniqueness. A winner that changes nothing becomes KEEP. It then gets the normal compression row orKEPT - NEEDS COMPRESSION. For example,ix9_abkeeps its compression advice,ix9_akeeps its DISABLE SCRIPT, anduq_t10_kkeeps its DISABLE CONSTRAINT SCRIPT.The winner is unwound when the foreign key guard keeps a loser (#913 follow-up)
The guard from #913 cleared
superseded_byon the loser row, which was already NULL. The claim and the merged include list are on the winner. So the winner still got a MERGE SCRIPT that added the includes of an index that stays.After the guard, the procedure now rebuilds the include list of each affected winner. The new list has the winner's own includes plus the includes of the losers that are still disabled. It rebuilds
superseded_byfrom the same losers. A MERGE INCLUDES winner with no loser left goes back to KEEP. The guard's comment no longer says that this already happened.If the winner backs the foreign key, the duplicate is still disabled. If both indexes back keys, neither is disabled.
rule_coverage_test.py no longer depends on uptime
Groups B and G now pass
@dedupe_only = 1. Group B has the two@min_writesand@min_readsruns on the write-only pair, and Group G has both multi-database runs. On a server that is up more than 7 days, Rule 1 disabled the never-read fixture indexes. The three assertions that failed on a server up 8 days now pass.Group E's fixture needed one change. Its subset now also includes
filler, because its merge added nothing, and such a merge is no longer emitted. The control still checks that the key column is not included. Group G's fixture still produces a real include merge and a real MAKE UNIQUE.New groups in rule_coverage_test.py:
ignore_dup_key,no_recomputeand the result of a duplicate insert.With dev's procedure installed before this change, the new rule_coverage_test.py had 66 passes and 15 failures. These are the 15 failures:
ix_sg_sup_abandux_sg_uqnoopux_a3_codegets a MERGE SCRIPT,ux_a3_codeis said to supersedeux_z3_code, and the winner where both indexes back keys is mergedThe positive controls, the K2 regression case and the three uptime assertions pass on both.
Changes by the coordinator
After #915 merged, dev was merged into this branch. It merged with no conflicts.
run_tests.pyassertion1-UC: 1d: Unique index merge has CREATE UNIQUEfailed with this change, because it expected the behavior that #906 removes. Its subset,ix_uc_ac, had no column thatuix_uc_acdlacks, so the merge changed nothing.ix_uc_acnow includescol_e, so the merge adds a column. Assertion 1d now also checks that the script includes[col_e].fixture_cases_test.pyno longer runs9-Executable: ix_test_7 (MERGE SCRIPT) executes, so it has 30 assertions, not 31.ix_test_7 (Age, CreationDate)already covers its subset, so it has no MERGE SCRIPT to run. The harness adds this assertion only when a MERGE SCRIPT exists. Three MERGE SCRIPTs still run in that harness:ix_c1,ix_test_3anduq_test_1.The coordinator also ran the generated scripts on SQL Server 2022, each inside a transaction that was rolled back:
ignore_dup_key = 1andno_recompute = 1. A duplicate insert was skipped with no error.IGNORE_DUP_KEY = ONkeptignore_dup_key = 1. A duplicate insert was skipped.no_recompute = 1.Tests
On SQL Server 2019 Developer, up 8 days, with dev and #915 merged in:
CI runs all four harnesses on SQL Server 2017, 2019, 2022 and 2025.
Not done
IGNORE_DUP_KEY = ONon a filtered index (Msg 10618). So that option never reaches the filtered-index script. The script carries it anyway, to match the MERGE SCRIPT. Its test coversSTATISTICS_NORECOMPUTEonly.Unique structures that differ in IGNORE_DUP_KEY
An earlier version of this description named one case that changes behavior. In that case, an index that is already unique and has
IGNORE_DUP_KEY = ONreplaces a strict unique constraint. The behavior does not change. On SQL Server 2022, a strict structure and an IGNORE_DUP_KEY structure can share their keys. Then SQL Server skips the duplicate row, and the strict structure raises no error. This held in 16 combinations, including inserts with a forced wide plan.The reverse cases do change behavior. In each one, the IGNORE_DUP_KEY structure is removed, and a strict one stays. Duplicate inserts then fail with error 2601 or 2627. These cases happen on dev before this change too, so they are in #916.
🤖 Generated with Claude Code
https://claude.ai/code/session_015SjCdz1y4HnJ5yNfFpN3Dv