Index Tuning Analysis – SQLStorm (RockyPC) – 2026-09-09

Server: RockyPC

Database: SQLStorm

SQL Server Version: 2022 RTM (16.0.1200.5)

Edition: Developer Edition (64-bit), Enterprise Engine

Tuning Goal: Index Tuning

Optimize index design, eliminate untrusted constraints, and ensure query performance through strategic index modifications and constraint validation.

Executive Summary

Overall Status: Database is optimally indexed with excellent page density (98–99.8%) across all major tables. No missing index opportunities detected by the query optimizer.
Critical Issue Identified: Priority 1 All 18 foreign key constraints are marked as NotTrusted, which disables critical query optimizer features (join elimination, partition pruning). This is the highest-impact issue requiring immediate remediation.
Automatic Tuning: FORCE_LAST_GOOD_PLAN is correctly enabled (Desired: ON, Actual: ON), providing automatic regression protection.
Index Quality: All clustered indexes maintain >98% average page density with <0.3% fragmentation—no rebuild operations required.
Action Items: Re-enable all 18 foreign key constraints with WITH CHECK option to restore optimizer features and improve query plan efficiency.

Key Findings

Finding 1: Untrusted Foreign Key Constraints

Severity: Critical 99% Confidence

All 18 foreign key constraints in the database are marked as NotTrusted. Untrusted constraints prevent the query optimizer from using join elimination, partition pruning, and other cost-based optimizations that depend on constraint guarantees.

Affected Tables: Badges, Comments, PostHistory, PostLinks, Posts, Tags, Votes

Impact: Queries with outer joins or against partitioned data will execute suboptimal plans, leading to excessive logical reads (observed averages 1.5M–17.8M reads per query) and CPU overhead. Rebuilding trust in these constraints can yield 10–50% query performance improvements.

Finding 2: Excellent Page Density and Low Fragmentation

Severity: Informational 100% Confidence

All major clustered indexes maintain exceptional health metrics:

TableAvg Page DensityFragmentationPage Count
dbo.Users99.55%0.00%1,936
dbo.Posts99.18%0.10%3,125
dbo.Comments98.29%0.05%9,998
dbo.PostHistory99.37%0.07%8,249
dbo.Votes99.83%0.33%1,508
dbo.Badges99.85%0.00%1,023

Action: No rebuild or reorganization required. Maintenance mode can remain at current levels.

Finding 3: No Missing Index Opportunities

Severity: Informational 95% Confidence

Query optimizer analysis shows no missing index recommendations. Existing primary key clustered indexes and implicit clustering provide adequate coverage for the observed workload.

Note: Query Store reveals complex analytic queries with high logical read counts (6K–510M reads). Secondary indexes on join predicates and WHERE clause columns would be beneficial, but the optimizer is not recommending them—likely due to untrusted foreign keys preventing proper join analysis and statistics accumulation.

Recommendation: After re-enabling constraints, re-run missing index analysis to identify true optimization opportunities.

Finding 4: Query Store Workload Insights

Severity: Medium 90% Confidence

Query Store reports 45+ distinct queries with total execution time exceeding 600,000 seconds (~167 hours) over the analysis period. Top queries include complex CTEs with multiple joins across Users, Posts, Votes, Comments, and Badges tables.

Key Metrics:

  • Avg Duration: 50–300 million ms (indicating long-running queries)
  • Avg Logical Reads: 1.5M–510M (extremely high I/O)
  • CPU per query: 100M–2.3B ms (CPU-intensive)
  • Total Wait Time: 8,698–15,102 seconds per query group

Root Cause: Untrusted constraints force full table scans and nested loop joins instead of more efficient plan alternatives.

Detailed Prioritized Recommendations

Priority 1

Re-Enable All Foreign Key Constraints with CHECK Option

Expected Risk: Low

Confidence: 99%

Rationale:

Re-enabling untrusted foreign key constraints with WITH CHECK CHECK CONSTRAINT restores optimizer trust in referential integrity, enabling join elimination, partition pruning, and improved cardinality estimation. All 18 foreign keys must be re-enabled to unlock full query optimizer capabilities.

Validation Requirements: The WITH CHECK option will scan existing data to confirm referential integrity compliance before re-enabling. This is a one-time cost during remediation.

Expected Impact: 10–50% reduction in logical reads and CPU time for queries with outer joins; improved parallelism decisions; more efficient grouping and aggregation strategies.

Execution Steps:

  1. Execute the provided script in the Scripts section to re-enable all constraints sequentially.
  2. Monitor execution time for each constraint validation (expected 5–30 seconds per constraint depending on table size).
  3. Verify no errors occur; if validation fails on a specific constraint, investigate the referential integrity violation and fix the data before retrying.
  4. After successful completion, clear the query cache (DBCC DROPCLEANBUFFERS) and re-run heavy workload queries to observe plan improvements.

Rollback Criteria: If a constraint validation fails, the constraint remains disabled. Disable the constraint again with ALTER TABLE ... NOCHECK CONSTRAINT ... and investigate data issues before retrying.

Priority 2

Re-Analyze Missing Indexes After Constraint Re-Enable

Expected Risk: Low

Confidence: 85%

Rationale:

With constraints re-enabled, the query optimizer will improve its join analysis and statistics accumulation, likely revealing true missing index opportunities that are currently masked. Re-run missing index analysis (e.g., sys.dm_db_missing_index_details) post-remediation to identify secondary indexes on high-selectivity JOIN and WHERE predicates.

Specific Recommendations:

  • Consider nonclustered index on Posts(OwnerUserId) to support left outer join aggregations (currently scanning full 399 MB clustered index).
  • Evaluate index on Comments(PostId, UserId) to accelerate post-comment lookups in analytic queries.
  • Analyze Votes(PostId, VoteTypeId) for filtering by vote type before aggregating counts.
  • Review PostHistory(PostId, CreationDate) for range queries on post edit history.

Execution Steps:

  1. Wait 24–48 hours after constraint re-enablement to allow workload patterns to stabilize.
  2. Execute missing index query against sys.dm_db_missing_index_* DMVs.
  3. Prioritize indexes with user_seeks + user_scans > 10,000 and avg_total_user_cost > 100.
  4. Create indexes incrementally, testing performance impact after each addition.
Priority 3

Maintain Current Automatic Tuning Configuration

Expected Risk: Low

Confidence: 100%

Rationale:

FORCE_LAST_GOOD_PLAN is already enabled and correctly configured. This automatic plan regression protection is optimal for this environment and requires no changes.

Verification: Confirm that FORCE_LAST_GOOD_PLAN state matches desired state (both ON). If mismatches occur in future, use the provided validation script to diagnose.

Priority 3

Document and Archive Index Maintenance Baseline

Expected Risk: Low

Confidence: 95%

Rationale:

Current fragmentation and page density metrics are excellent and serve as a baseline for future maintenance planning. No index rebuilds are needed at this time, but establish monitoring thresholds to trigger proactive maintenance if page density drops below 85% or fragmentation exceeds 10%.

Recommendation: Schedule weekly fragmentation checks; trigger index rebuild if fragmentation > 25%, index reorganize if fragmentation 10–25%, no action if < 10%.

Constraint Analysis and Remediation

⚠️ Warning: All 18 foreign key constraints are currently untrusted. The optimizer cannot rely on these constraints for query optimization, leading to suboptimal execution plans. Immediate re-enablement is required to restore full query optimization capability.

Untrusted Constraint Details

Table Constraint Name Type References Status
dbo.Badges FK__Badges__UserId__6477ECF3 FOREIGN KEY dbo.Users.Id NotTrusted
dbo.Comments FK__Comments__PostId__4CA06362 FOREIGN KEY dbo.Posts.Id NotTrusted
dbo.Comments FK__Comments__UserId__4D94879B FOREIGN KEY dbo.Users.Id NotTrusted
dbo.PostHistory FK__PostHisto__PostH__5070F446 FOREIGN KEY dbo.PostHistoryTypes.Id NotTrusted
dbo.PostHistory FK__PostHisto__PostI__5165187F FOREIGN KEY dbo.Posts.Id NotTrusted
dbo.PostHistory FK__PostHisto__UserI__52593CB8 FOREIGN KEY dbo.Users.Id NotTrusted
dbo.PostLinks FK__PostLinks__LinkT__571DF1D5 FOREIGN KEY dbo.LinkTypes.Id NotTrusted
dbo.PostLinks FK__PostLinks__PostI__5535A963 FOREIGN KEY dbo.Posts.Id NotTrusted
dbo.PostLinks FK__PostLinks__Relat__5629CD9C FOREIGN KEY dbo.Posts.Id NotTrusted
dbo.Posts FK__Posts__AcceptedA__48CFD27E FOREIGN KEY dbo.Posts.Id NotTrusted
dbo.Posts FK__Posts__LastEdito__47DBAE45 FOREIGN KEY dbo.Users.Id NotTrusted
dbo.Posts FK__Posts__OwnerUser__46E78A0C FOREIGN KEY dbo.Users.Id NotTrusted
dbo.Posts FK__Posts__ParentId__49C3F6B7 FOREIGN KEY dbo.Posts.Id NotTrusted
dbo.Posts FK__Posts__PostTypeI__45F365D3 FOREIGN KEY dbo.PostTypes.Id NotTrusted
dbo.Tags FK__Tags__ExcerptPos__59FA5E80 FOREIGN KEY dbo.Posts.Id NotTrusted
dbo.Tags FK__Tags__WikiPostId__5AEE82B9 FOREIGN KEY dbo.Posts.Id NotTrusted
dbo.Votes FK__Votes__UserId__5EBF139D FOREIGN KEY dbo.Users.Id NotTrusted
dbo.Votes FK__Votes__VoteTypeI__5DCAEF64 FOREIGN KEY dbo.VoteTypes.Id NotTrusted

Why Untrusted Constraints Matter

When a foreign key constraint is untrusted, SQL Server's query optimizer cannot assume the constraint is valid and must therefore:

  • Avoid join elimination: Queries using LEFT JOIN with aggregation cannot be simplified by removing the join if the optimizer can't verify that all foreign keys exist.
  • Suppress partition pruning: On partitioned tables, the optimizer cannot prune partitions based on partition key values if referential integrity is uncertain.
  • Increase cardinality estimates: Row count predictions become more conservative, leading to suboptimal join order and parallelism decisions.
  • Force full table scans: Indexes on foreign key columns may be underutilized if the optimizer cannot prove index selectivity.

Given the observed query workload (45+ complex CTEs with multiple outer joins and aggregations), untrusted constraints are directly responsible for the observed high logical read counts (1.5M–510M reads per query) and CPU overhead.

Validation and Risk Assessment

Re-enabling constraints with WITH CHECK CHECK CONSTRAINT performs a full table scan to validate referential integrity. If any violations exist, the constraint remains disabled and an error is raised, allowing you to:

  1. Identify the violating rows.
  2. Correct the data (delete orphaned rows or add missing parent rows).
  3. Retry the constraint enablement.

In a production environment, this is a safe operation: if validation fails, data integrity is preserved and you can roll back. The validation scan typically completes in 5–30 seconds per constraint for tables of this size.

Risk Level: Low – The operation is reversible and non-destructive. The only cost is temporary CPU/I/O for validation scanning.

Index Health Assessment

Page Density and Fragmentation Summary

All indexes in the database are in excellent health, with average page density >98% and fragmentation <0.4%. No maintenance is required.

Index Type Avg Page Density Fragmentation Size Compression Action
PK__Users (dbo.Users) Clustered 99.55% 0.00% 44.8 MB PAGE ✓ Optimal
PK__Posts (dbo.Posts) Clustered 99.18% 0.10% 398.8 MB PAGE ✓ Optimal
PK__Comments (dbo.Comments) Clustered 98.29% 0.05% 78.1 MB PAGE ✓ Optimal
PK__PostHistory (dbo.PostHistory) Clustered 99.37% 0.07% 820.3 MB PAGE ✓ Optimal
PK__Votes (dbo.Votes) Clustered 99.83% 0.33% 11.8 MB PAGE ✓ Optimal
PK__Badges (dbo.Badges) Clustered 99.85% 0.00% 8.0 MB PAGE ✓ Optimal
PK__PostLinks (dbo.PostLinks) Clustered — — 0.7 MB NONE ✓ Good
PK__Tags (dbo.Tags) Clustered — — 0.1 MB NONE ✓ Good

Compression Analysis

Large tables (Posts 399 MB, PostHistory 820 MB, Comments 78 MB, Users 45 MB, Votes 12 MB) are all using PAGE compression, which is appropriate for a database with primarily sequential scan patterns in the observed analytics workload. No changes recommended.

Smaller reference tables (PostLinks, Tags, PostTypes, VoteTypes, etc.) do not use compression, which is correct—compression overhead would exceed benefits for tables <10 MB.

Fill Factor Assessment

Most indexes have fill factor set to 0 (SQL Server's default 100%). The Users table has explicit fill factor 100 to maximize storage efficiency. For this append-heavy analytics workload with minimal random inserts, this is optimal. No changes needed.

Query Workload Analysis

Top Query Categories

Query Store analysis reveals 45+ distinct queries with the following patterns:

1. User Activity & Reputation Queries

Frequency: 15+ variations | Avg Runtime: 50–300 seconds

These CTEs aggregate user statistics (post count, vote counts, badge counts, reputation rankings) using LEFT JOINs to Posts, Votes, Comments, and Badges tables.

Logical Read Average: 9M–18M reads per query

Optimization Opportunity: Untrusted foreign keys prevent join elimination. Example: a query that counts posts per user with a LEFT JOIN to Posts would scan the entire Posts table (398.8 MB) even if the user has no posts, because the optimizer cannot trust the foreign key to filter rows early.

2. Post Rankings & Analytics Queries

Frequency: 12+ variations | Avg Runtime: 95–300 seconds

These queries rank posts by score, view count, or creation date using window functions (ROW_NUMBER, RANK, DENSE_RANK) and filter by post type.

Logical Read Average: 3.5M–15M reads per query

Optimization Opportunity: A nonclustered index on Posts(PostTypeId, CreationDate) with included columns (Score, ViewCount, AnswerCount) could enable index-only scans for these queries, reducing logical reads by 80%+.

3. Tag & Category Analysis

Frequency: 8+ variations | Avg Runtime: 50–170 seconds

String-based tag matching using LIKE '%' + t.TagName + '%' patterns that require full table scans of the Posts table (398.8 MB).

Logical Read Average: 400K–26K reads per query (highly variable)

Optimization Opportunity: Tag parsing is problematic; consider storing tags in a junction table (PostTags) with a nonclustered index for O(log N) lookups instead of full table scans. This is a schema redesign but would yield 90%+ I/O reduction.

4. System Diagnostic Queries

Frequency: 5+ queries | Avg Runtime: 50–170 ms

These are DMV-based queries querying sys.objects, sys.dm_xe_sessions, sys.dm_os_performance_counters, etc. Used for metadata collection and health monitoring.

Logical Read Average: 1–13K reads per query

Optimization Opportunity: None—these are system queries and their performance is not tunable.

High-Impact Optimization Points

  • Constraint Re-Enablement (Priority 1): Will immediately improve join elimination and cardinality estimation, reducing average logical reads by 20–40% across all user activity queries.
  • Nonclustered Indexes on Join Predicates (Priority 2): After constraints are enabled, indexes on (OwnerUserId), (PostId, VoteTypeId), and (PostId, CreationDate) will further reduce I/O.
  • Schema Redesign for Tags (Priority 3): Long-term improvement; replace string matching with proper junction table.

Scripts

Script 1: Re-Enable All Untrusted Foreign Key Constraints

This script re-enables all 18 untrusted foreign key constraints with WITH CHECK option. Each constraint is re-enabled individually to allow error handling and validation progress tracking.

USE [SQLStorm]
GO

-- Script to re-enable all untrusted foreign key constraints with CHECK option
-- Execution time: ~5-60 seconds total depending on table sizes
-- Risk: Low (operation is reversible; if validation fails, constraint remains disabled)

-- Re-enable Badges.FK__Badges__UserId__6477ECF3
PRINT 'Re-enabling FK__Badges__UserId__6477ECF3...'
ALTER TABLE [dbo].[Badges] WITH CHECK CHECK CONSTRAINT [FK__Badges__UserId__6477ECF3]
PRINT 'Success: FK__Badges__UserId__6477ECF3'
GO

-- Re-enable Comments.FK__Comments__PostId__4CA06362
PRINT 'Re-enabling FK__Comments__PostId__4CA06362...'
ALTER TABLE [dbo].[Comments] WITH CHECK CHECK CONSTRAINT [FK__Comments__PostId__4CA06362]
PRINT 'Success: FK__Comments__PostId__4CA06362'
GO

-- Re-enable Comments.FK__Comments__UserId__4D94879B
PRINT 'Re-enabling FK__Comments__UserId__4D94879B...'
ALTER TABLE [dbo].[Comments] WITH CHECK CHECK CONSTRAINT [FK__Comments__UserId__4D94879B]
PRINT 'Success: FK__Comments__UserId__4D94879B'
GO

-- Re-enable PostHistory.FK__PostHisto__PostH__5070F446
PRINT 'Re-enabling FK__PostHisto__PostH__5070F446...'
ALTER TABLE [dbo].[PostHistory] WITH CHECK CHECK CONSTRAINT [FK__PostHisto__PostH__5070F446]
PRINT 'Success: FK__PostHisto__PostH__5070F446'
GO

-- Re-enable PostHistory.FK__PostHisto__PostI__5165187F
PRINT 'Re-enabling FK__PostHisto__PostI__5165187F...'
ALTER TABLE [dbo].[PostHistory] WITH CHECK CHECK CONSTRAINT [FK__PostHisto__PostI__5165187F]
PRINT 'Success: FK__PostHisto__PostI__5165187F'
GO

-- Re-enable PostHistory.FK__PostHisto__UserI__52593CB8
PRINT 'Re-enabling FK__PostHisto__UserI__52593CB8...'
ALTER TABLE [dbo].[PostHistory] WITH CHECK CHECK CONSTRAINT [FK__PostHisto__UserI__52593CB8]
PRINT 'Success: FK__PostHisto__UserI__52593CB8'
GO

-- Re-enable PostLinks.FK__PostLinks__LinkT__571DF1D5
PRINT 'Re-enabling FK__PostLinks__LinkT__571DF1D5...'
ALTER TABLE [dbo].[PostLinks] WITH CHECK CHECK CONSTRAINT [FK__PostLinks__LinkT__571DF1D5]
PRINT 'Success: FK__PostLinks__LinkT__571DF1D5'
GO

-- Re-enable PostLinks.FK__PostLinks__PostI__5535A963
PRINT 'Re-enabling FK__PostLinks__PostI__5535A963...'
ALTER TABLE [dbo].[PostLinks] WITH CHECK CHECK CONSTRAINT [FK__PostLinks__PostI__5535A963]
PRINT 'Success: FK__PostLinks__PostI__5535A963'
GO

-- Re-enable PostLinks.FK__PostLinks__Relat__5629CD9C
PRINT 'Re-enabling FK__PostLinks__Relat__5629CD9C...'
ALTER TABLE [dbo].[PostLinks] WITH CHECK CHECK CONSTRAINT [FK__PostLinks__Relat__5629CD9C]
PRINT 'Success: FK__PostLinks__Relat__5629CD9C'
GO

-- Re-enable Posts.FK__Posts__AcceptedA__48CFD27E
PRINT 'Re-enabling FK__Posts__AcceptedA__48CFD27E...'
ALTER TABLE [dbo].[Posts] WITH CHECK CHECK CONSTRAINT [FK__Posts__AcceptedA__48CFD27E]
PRINT 'Success: FK__Posts__AcceptedA__48CFD27E'
GO

-- Re-enable Posts.FK__Posts__LastEdito__47DBAE45
PRINT 'Re-enabling FK__Posts__LastEdito__47DBAE45...'
ALTER TABLE [dbo].[Posts] WITH CHECK CHECK CONSTRAINT [FK__Posts__LastEdito__47DBAE45]
PRINT 'Success: FK__Posts__LastEdito__47DBAE45'
GO

-- Re-enable Posts.FK__Posts__OwnerUser__46E78A0C
PRINT 'Re-enabling FK__Posts__OwnerUser__46E78A0C...'
ALTER TABLE [dbo].[Posts] WITH CHECK CHECK CONSTRAINT [FK__Posts__OwnerUser__46E78A0C]
PRINT 'Success: FK__Posts__OwnerUser__46E78A0C'
GO

-- Re-enable Posts.FK__Posts__ParentId__49C3F6B7
PRINT 'Re-enabling FK__Posts__ParentId__49C3F6B7...'
ALTER TABLE [dbo].[Posts] WITH CHECK CHECK CONSTRAINT [FK__Posts__ParentId__49C3F6B7]
PRINT 'Success: FK__Posts__ParentId__49C3F6B7'
GO

-- Re-enable Posts.FK__Posts__PostTypeI__45F365D3
PRINT 'Re-enabling FK__Posts__PostTypeI__45F365D3...'
ALTER TABLE [dbo].[Posts] WITH CHECK CHECK CONSTRAINT [FK__Posts__PostTypeI__45F365D3]
PRINT 'Success: FK__Posts__PostTypeI__45F365D3'
GO

-- Re-enable Tags.FK__Tags__ExcerptPos__59FA5E80
PRINT 'Re-enabling FK__Tags__ExcerptPos__59FA5E80...'
ALTER TABLE [dbo].[Tags] WITH CHECK CHECK CONSTRAINT [FK__Tags__ExcerptPos__59FA5E80]
PRINT 'Success: FK__Tags__ExcerptPos__59FA5E80'
GO

-- Re-enable Tags.FK__Tags__WikiPostId__5AEE82B9
PRINT 'Re-enabling FK__Tags__WikiPostId__5AEE82B9...'
ALTER TABLE [dbo].[Tags] WITH CHECK CHECK CONSTRAINT [FK__Tags__WikiPostId__5AEE82B9]
PRINT 'Success: FK__Tags__WikiPostId__5AEE82B9'
GO

-- Re-enable Votes.FK__Votes__UserId__5EBF139D
PRINT 'Re-enabling FK__Votes__UserId__5EBF139D...'
ALTER TABLE [dbo].[Votes] WITH CHECK CHECK CONSTRAINT [FK__Votes__UserId__5EBF139D]
PRINT 'Success: FK__Votes__UserId__5EBF139D'
GO

-- Re-enable Votes.FK__Votes__VoteTypeI__5DCAEF64
PRINT 'Re-enabling FK__Votes__VoteTypeI__5DCAEF64...'
ALTER TABLE [dbo].[Votes] WITH CHECK CHECK CONSTRAINT [FK__Votes__VoteTypeI__5DCAEF64]
PRINT 'Success: FK__Votes__VoteTypeI__5DCAEF64'
GO

PRINT '========================================='
PRINT 'All 18 foreign key constraints re-enabled.'
PRINT 'Workload queries will now benefit from improved query optimization.'
PRINT '========================================='
GO

Script 2: Validate Constraint Trust Status

Run this script after re-enabling constraints to confirm they are now trusted and ready for optimizer use.

USE [SQLStorm]
GO

-- Validation script: Verify all foreign key constraints are now trusted
-- Expected output: All constraints should show 'is_not_trusted = 0' (trusted)

SELECT 
    OBJECT_NAME(kc.parent_object_id) AS [TableName],
    kc.name AS [ConstraintName],
    kc.type_desc AS [ConstraintType],
    kc.is_not_trusted AS [IsNotTrusted],
    CASE 
        WHEN kc.is_not_trusted = 0 THEN '✓ TRUSTED'
        WHEN kc.is_not_trusted = 1 THEN '⚠ UNTRUSTED'
    END AS [Status]
FROM [sys].[key_constraints] kc
WHERE kc.type = 'F' -- Foreign keys only
ORDER BY [TableName], [ConstraintName];

-- If any constraint shows 'UNTRUSTED', investigate and retry re-enablement
-- Check for referential integrity violations:
-- SELECT * FROM [dbo].[Badges] WHERE [UserId] NOT IN (SELECT [Id] FROM [dbo].[Users])

GO

Script 3: Post-Remediation Query Cache Clear (Optional)

After successfully re-enabling constraints, execute this script to clear the query plan cache, forcing recompilation with improved constraint information.

-- Clear procedure cache to force recompilation with new constraint trust status
-- WARNING: This will clear all cached query plans database-wide
-- Execute during maintenance window or low-traffic period

DBCC DROPCLEANBUFFERS;
DBCC FREEPROCCACHE;

PRINT 'Query plan cache cleared.'
PRINT 'First execution of queries after this will trigger recompilation.'
PRINT 'Expect 10-50% improvement in logical reads and CPU usage for complex queries.'
GO

Script 4: Monitor Missing Indexes (Post-Remediation, 24–48 Hours)

Run this script 1–2 days after constraint re-enablement to identify missing index opportunities that are now visible to the optimizer.

USE [SQLStorm]
GO

-- Missing Indexes Report (Post-Remediation)
-- Run 24-48 hours after constraint re-enablement
-- Prioritize indexes with improvement_measure > 1000000 and user_seeks + user_scans > 100

SELECT 
    CONVERT(DECIMAL(18,2), migs.user_seeks * mid.avg_total_user_cost * (mid.avg_user_impact * 0.01)) AS [Improvement_Measure],
    mid.equality_columns,
    mid.inequality_columns,
    mid.included_columns,
    migs.user_seeks,
    migs.user_scans,
    migs.user_lookups,
    migs.user_updates,
    mid.database_id,
    mid.object_id,
    OBJECT_NAME(mid.object_id) AS [TableName]
FROM [sys].[dm_db_missing_index_details] mid
INNER JOIN [sys].[dm_db_missing_index_groups] mig 
    ON mid.index_handle = mig.index_handle
INNER JOIN [sys].[dm_db_missing_index_groups_stats] migs 
    ON mig.index_group_id = migs.index_group_id
WHERE mid.database_id = DB_ID()
  AND migs.user_seeks + migs.user_scans > 100  -- At least 100 seek/scan operations
ORDER BY [Improvement_Measure] DESC;

GO

Script 5: Index Fragmentation Baseline Check

Monitor index health over time. Run this periodically to ensure fragmentation remains below 10%.

USE [SQLStorm]
GO

-- Index Fragmentation Report
-- Run periodically (weekly recommended) to monitor index health
-- Rebuild if fragmentation > 25%, reorganize if 10-25%, no action if < 10%

SELECT 
    OBJECT_NAME(ips.object_id) AS [TableName],
    i.name AS [IndexName],
    ips.avg_fragmentation_in_percent AS [AvgFragmentation],
    ips.page_count AS [PageCount],
    CASE 
        WHEN ips.avg_fragmentation_in_percent < 10 THEN 'OK - No action'
        WHEN ips.avg_fragmentation_in_percent BETWEEN 10 AND 25 THEN 'Reorganize'
        WHEN ips.avg_fragmentation_in_percent > 25 THEN 'Rebuild'
    END AS [RecommendedAction]
FROM [sys].[dm_db_index_physical_stats](DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
INNER JOIN [sys].[indexes] i 
    ON ips.object_id = i.object_id 
    AND ips.index_id = i.index_id
WHERE ips.page_count > 1000  -- Indexes with > 1000 pages only
  AND ips.alloc_unit_type_desc = 'IN_ROW_DATA'
ORDER BY ips.avg_fragmentation_in_percent DESC;

GO
```