SQL Server - Auto-Generate Missing Index Scripts
A query that reads from SQL Server’s missing index DMVs and auto-generates ready-to-run CREATE INDEX statements. Results are ranked by impact score so you know where to focus first.
What it does
- Reads from
sys.dm_db_missing_index_detailsand related DMVs - Consolidates suggestions per table to avoid index sprawl
- Generates
CREATE INDEXstatements withDATA_COMPRESSION = PAGE - Flags results as HIGH PRIORITY, RECOMMENDED, or NOTE: Low usage pattern
The Query
WITH MissingIndexes
AS (SELECT
DB_NAME (dm_mid.database_id) AS DatabaseName
, dm_mid.database_id AS DatabaseID
, OBJECT_SCHEMA_NAME (dm_mid.object_id, dm_mid.database_id) AS SchemaName
, OBJECT_NAME (dm_mid.object_id, dm_mid.database_id) AS TableName
, dm_migs.avg_user_impact
, dm_migs.user_seeks
, dm_migs.user_scans
, dm_migs.last_user_seek
, dm_migs.last_user_scan
, CONVERT (DECIMAL(18, 2), dm_migs.avg_user_impact * (dm_migs.user_seeks + dm_migs.user_scans) / 100.0) AS impact_score
, dm_migs.user_seeks + dm_migs.user_scans AS total_reads
, ISNULL (dm_mid.equality_columns, '') AS equality_columns
, ISNULL (dm_mid.inequality_columns, '') AS inequality_columns
, ISNULL (dm_mid.included_columns, '') AS included_columns
, dm_mid.statement
, ROW_NUMBER () OVER (PARTITION BY dm_mid.database_id, dm_mid.object_id
ORDER BY dm_migs.avg_user_impact * (dm_migs.user_seeks + dm_migs.user_scans) DESC
) AS rank_in_table
, CONVERT (VARCHAR(10), ABS (CHECKSUM (
ISNULL (dm_mid.equality_columns, '')
+ ISNULL (dm_mid.inequality_columns, '')
+ ISNULL (dm_mid.included_columns, ''))) % 1000000000) AS index_suffix
FROM sys.dm_db_missing_index_groups dm_mig
INNER JOIN sys.dm_db_missing_index_group_stats dm_migs ON dm_migs.group_handle = dm_mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details dm_mid ON dm_mig.index_handle = dm_mid.index_handle
INNER JOIN sys.databases d ON d.database_id = dm_mid.database_id
WHERE d.is_read_only = 0 AND d.state_desc = 'ONLINE')
, ConsolidatedIndexes
AS (SELECT
DatabaseName, DatabaseID, SchemaName, TableName
, STRING_AGG (
CASE WHEN rank_in_table = 1 THEN
'CREATE INDEX [IX_MMC_' + TableName + '_' + CONVERT (VARCHAR(10), index_suffix)
+ '] ON ' + statement + ' ('
+ CASE WHEN equality_columns != '' THEN equality_columns ELSE '' END
+ CASE WHEN inequality_columns != '' AND equality_columns != '' THEN ', ' + inequality_columns
WHEN inequality_columns != '' THEN inequality_columns ELSE '' END + ')'
+ CASE WHEN included_columns != '' THEN ' INCLUDE (' + included_columns + ')' ELSE '' END
+ ' WITH (DATA_COMPRESSION = PAGE)'
END, CHAR(13)) AS consolidated_create_statement
, SUM (impact_score) AS total_impact_score
, MAX (last_user_seek) AS latest_seek
, SUM (total_reads) AS total_reads
, COUNT (*) AS suggested_index_count
, MAX (equality_columns) AS key_columns
, MAX (included_columns) AS included_columns
FROM MissingIndexes
WHERE rank_in_table = 1
GROUP BY DatabaseName, DatabaseID, SchemaName, TableName)
SELECT
DatabaseName, DatabaseID, SchemaName, TableName
, total_impact_score AS consolidated_impact_score
, total_reads
, latest_seek
, suggested_index_count AS original_suggestion_count
, key_columns AS proposed_key_columns
, included_columns AS proposed_included_columns
, consolidated_create_statement
, CASE WHEN total_reads < 1000 THEN 'NOTE: Low usage pattern'
WHEN total_impact_score > 5000 THEN 'HIGH PRIORITY'
ELSE 'RECOMMENDED'
END AS implementation_note
FROM ConsolidatedIndexes
WHERE consolidated_create_statement IS NOT NULL
ORDER BY implementation_note DESC, latest_seek, total_impact_score DESC;
Output columns
| Column | Description |
|---|---|
consolidated_impact_score |
Higher = more benefit from adding the index |
total_reads |
Seeks + scans driving the recommendation |
implementation_note |
HIGH PRIORITY / RECOMMENDED / Low usage pattern |
consolidated_create_statement |
Ready-to-run CREATE INDEX script |
Notes
- Only queries ONLINE, writable databases
- Index names use the
IX_MMC_prefix — change to match your naming convention - Always review and test in a non-production environment before running
- DMV data resets on SQL Server service restart