Created
June 3, 2026 14:27
-
-
Save ststeiger/6b8851a30f00bccbb5e6d371a62a3ba9 to your computer and use it in GitHub Desktop.
How to diff indexes between two MS-SQL DBs
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
| /* | |
| SELECT | |
| s.name AS TABLE_SCHEMA | |
| ,t.name AS TABLE_NAME | |
| ,i.name AS INDEX_NAME | |
| ,i.type_desc AS INDEX_TYPE | |
| ,i.is_unique AS IS_UNIQUE | |
| ,i.is_primary_key AS IS_PRIMARY_KEY | |
| ,i.is_unique_constraint AS IS_UNIQUE_CONSTRAINT | |
| ,i.filter_definition AS FILTER_DEFINITION | |
| ,ic.key_ordinal AS KEY_ORDINAL | |
| ,ic.is_descending_key AS IS_DESCENDING | |
| ,ic.is_included_column AS IS_INCLUDED | |
| ,c.name AS COLUMN_NAME | |
| FROM sys.tables AS t | |
| INNER JOIN sys.schemas AS s ON s.schema_id = t.schema_id | |
| INNER JOIN sys.indexes AS i ON i.object_id = t.object_id | |
| INNER JOIN sys.index_columns AS ic ON ic.object_id = i.object_id | |
| AND ic.index_id = i.index_id | |
| INNER JOIN sys.columns AS c ON c.object_id = ic.object_id | |
| AND c.column_id = ic.column_id | |
| WHERE (1=1) | |
| -- AND t.name LIKE 'T_Checklist%' | |
| AND i.type > 0 -- exclude heaps | |
| ORDER BY TABLE_NAME, INDEX_NAME, KEY_ORDINAL | |
| -- FOR JSON AUTO | |
| FOR JSON PATH; | |
| -- FOR JSON PATH, ROOT('indexes'); -- , INCLUDE_NULL_VALUES; | |
| -- FOR XML PATH('row'), ROOT('table'), ELEMENTS xsinil | |
| */ | |
| DECLARE @db1JSON nvarchar(MAX) = N'[ ... ]'; | |
| DECLARE @db2JSON nvarchar(MAX) = N'[ ... ]'; | |
| SELECT * | |
| INTO #idx1 | |
| FROM OPENJSON(@db1JSON) | |
| WITH | |
| ( | |
| TABLE_SCHEMA nvarchar(128) '$.TABLE_SCHEMA' | |
| ,TABLE_NAME nvarchar(128) '$.TABLE_NAME' | |
| ,INDEX_NAME nvarchar(128) '$.INDEX_NAME' | |
| ,COLUMN_NAME nvarchar(128) '$.COLUMN_NAME' | |
| ,INDEX_TYPE nvarchar(60) '$.INDEX_TYPE' | |
| ,IS_UNIQUE bit '$.IS_UNIQUE' | |
| ,IS_PRIMARY_KEY bit '$.IS_PRIMARY_KEY' | |
| ,IS_UNIQUE_CONSTRAINT bit '$.IS_UNIQUE_CONSTRAINT' | |
| ,FILTER_DEFINITION nvarchar(MAX) '$.FILTER_DEFINITION' | |
| ,KEY_ORDINAL int '$.KEY_ORDINAL' | |
| ,IS_DESCENDING bit '$.IS_DESCENDING' | |
| ,IS_INCLUDED bit '$.IS_INCLUDED' | |
| ); | |
| SELECT * | |
| INTO #idx2 | |
| FROM OPENJSON(@db2JSON) | |
| WITH | |
| ( | |
| TABLE_SCHEMA nvarchar(128) '$.TABLE_SCHEMA' | |
| ,TABLE_NAME nvarchar(128) '$.TABLE_NAME' | |
| ,INDEX_NAME nvarchar(128) '$.INDEX_NAME' | |
| ,COLUMN_NAME nvarchar(128) '$.COLUMN_NAME' | |
| ,INDEX_TYPE nvarchar(60) '$.INDEX_TYPE' | |
| ,IS_UNIQUE bit '$.IS_UNIQUE' | |
| ,IS_PRIMARY_KEY bit '$.IS_PRIMARY_KEY' | |
| ,IS_UNIQUE_CONSTRAINT bit '$.IS_UNIQUE_CONSTRAINT' | |
| ,FILTER_DEFINITION nvarchar(MAX) '$.FILTER_DEFINITION' | |
| ,KEY_ORDINAL int '$.KEY_ORDINAL' | |
| ,IS_DESCENDING bit '$.IS_DESCENDING' | |
| ,IS_INCLUDED bit '$.IS_INCLUDED' | |
| ); | |
| SELECT | |
| COALESCE(d1.TABLE_NAME, d2.TABLE_NAME) AS TABLE_NAME | |
| ,COALESCE(d1.INDEX_NAME, d2.INDEX_NAME) AS INDEX_NAME | |
| ,COALESCE(d1.COLUMN_NAME, d2.COLUMN_NAME) AS COLUMN_NAME | |
| ,CASE | |
| WHEN d1.INDEX_NAME IS NULL THEN '➕ Only in DB2' | |
| WHEN d2.INDEX_NAME IS NULL THEN '➖ Only in DB1' | |
| ELSE '⚠️ Different' | |
| END AS diff_type | |
| ,d1.INDEX_TYPE AS db1_INDEX_TYPE | |
| ,d1.IS_UNIQUE AS db1_IS_UNIQUE | |
| ,d1.IS_PRIMARY_KEY AS db1_IS_PRIMARY_KEY | |
| ,d1.KEY_ORDINAL AS db1_KEY_ORDINAL | |
| ,d1.IS_DESCENDING AS db1_IS_DESCENDING | |
| ,d1.IS_INCLUDED AS db1_IS_INCLUDED | |
| ,d1.FILTER_DEFINITION AS db1_FILTER | |
| ,d2.INDEX_TYPE AS db2_INDEX_TYPE | |
| ,d2.IS_UNIQUE AS db2_IS_UNIQUE | |
| ,d2.IS_PRIMARY_KEY AS db2_IS_PRIMARY_KEY | |
| ,d2.KEY_ORDINAL AS db2_KEY_ORDINAL | |
| ,d2.IS_DESCENDING AS db2_IS_DESCENDING | |
| ,d2.IS_INCLUDED AS db2_IS_INCLUDED | |
| ,d2.FILTER_DEFINITION AS db2_FILTER | |
| FROM #idx1 AS d1 | |
| FULL JOIN #idx2 AS d2 | |
| ON d1.TABLE_NAME = d2.TABLE_NAME | |
| AND d1.INDEX_NAME = d2.INDEX_NAME | |
| AND d1.COLUMN_NAME = d2.COLUMN_NAME | |
| WHERE (1=2) | |
| -- Only show rows that actually differ | |
| OR d1.INDEX_NAME IS NULL -- missing from DB1 | |
| OR d2.INDEX_NAME IS NULL -- missing from DB2 | |
| OR d1.INDEX_TYPE <> d2.INDEX_TYPE | |
| OR d1.IS_UNIQUE <> d2.IS_UNIQUE | |
| OR d1.IS_INCLUDED <> d2.IS_INCLUDED | |
| OR d1.IS_DESCENDING <> d2.IS_DESCENDING | |
| OR ISNULL(d1.KEY_ORDINAL, -999) <> ISNULL(d2.KEY_ORDINAL, -999) | |
| OR ISNULL(d1.FILTER_DEFINITION, '~~NULL~~') <> ISNULL(d2.FILTER_DEFINITION, '~~NULL~~') | |
| ORDER BY | |
| COALESCE(d1.TABLE_NAME, d2.TABLE_NAME) | |
| ,COALESCE(d1.INDEX_NAME, d2.INDEX_NAME) | |
| ,COALESCE(d1.KEY_ORDINAL, d2.KEY_ORDINAL); | |
| DROP TABLE #idx1, #idx2; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment