Hi TSQL gurus,
I have a requirement to come up with reporting for indexes for our whole production environment:
1. I found the below script online and modified it according to my requirement and trying to execute it using sp_MSForEachDb undocumented SP so that I can get result from all databases in the instance. The script should show all Exact Duplicate
indexes in a particular database. I'm getting the below error (see below script).
Script:
--This query will find exact duplicate indexes with exact Key Column List and Exact Included Columns.
--Source: https://www.sqlservercentral.com/articles/finding-and-eliminating-duplicate-or-overlapping-indexes-1
SET NOCOUNT ON
EXEC sp_MSForEachDb 'USE [?]
IF ''?'' NOT IN (''master'', ''model'', ''msdb'', ''tempdb'')
BEGIN
IF OBJECT_ID(''tempdb..#IndexTemp'') IS NOT NULL DROP Table #IndexTemp --If exist drop the temp table.
;WITH CTE_INDEX_DATA AS (
SELECT
SCHEMA_DATA.name AS Schema_Name,
TABLE_DATA.name AS Table_Name,
INDEX_DATA.name AS Index_Name,
STUFF((SELECT ', ' + COLUMN_DATA_KEY_COLS.name + '' + CASE WHEN INDEX_COLUMN_DATA_KEY_COLS.is_descending_key = 1 THEN ''DESC'' ELSE ''ASC'' END -- Include column order (ASC / DESC)
FROM sys.tables AS T
INNER JOIN sys.indexes INDEX_DATA_KEY_COLS
ON T.object_id = INDEX_DATA_KEY_COLS.object_id
INNER JOIN sys.index_columns INDEX_COLUMN_DATA_KEY_COLS
ON INDEX_DATA_KEY_COLS.object_id = INDEX_COLUMN_DATA_KEY_COLS.object_id
AND INDEX_DATA_KEY_COLS.index_id = INDEX_COLUMN_DATA_KEY_COLS.index_id
INNER JOIN sys.columns COLUMN_DATA_KEY_COLS
ON T.object_id = COLUMN_DATA_KEY_COLS.object_id
AND INDEX_COLUMN_DATA_KEY_COLS.column_id = COLUMN_DATA_KEY_COLS.column_id
WHERE INDEX_DATA.object_id = INDEX_DATA_KEY_COLS.object_id
AND INDEX_DATA.index_id = INDEX_DATA_KEY_COLS.index_id
AND INDEX_COLUMN_DATA_KEY_COLS.is_included_column = 0
ORDER BY INDEX_COLUMN_DATA_KEY_COLS.key_ordinal
FOR XML PATH('')), 1, 2, '') AS Key_Column_List ,
STUFF(( SELECT ', ' + COLUMN_DATA_INC_COLS.name
FROM sys.tables AS T
INNER JOIN sys.indexes INDEX_DATA_INC_COLS
ON T.object_id = INDEX_DATA_INC_COLS.object_id
INNER JOIN sys.index_columns INDEX_COLUMN_DATA_INC_COLS
ON INDEX_DATA_INC_COLS.object_id = INDEX_COLUMN_DATA_INC_COLS.object_id
AND INDEX_DATA_INC_COLS.index_id = INDEX_COLUMN_DATA_INC_COLS.index_id
INNER JOIN sys.columns COLUMN_DATA_INC_COLS
ON T.object_id = COLUMN_DATA_INC_COLS.object_id
AND INDEX_COLUMN_DATA_INC_COLS.column_id = COLUMN_DATA_INC_COLS.column_id
WHERE INDEX_DATA.object_id = INDEX_DATA_INC_COLS.object_id
AND INDEX_DATA.index_id = INDEX_DATA_INC_COLS.index_id
AND INDEX_COLUMN_DATA_INC_COLS.is_included_column = 1
ORDER BY INDEX_COLUMN_DATA_INC_COLS.key_ordinal
FOR XML PATH('')), 1, 2, '') AS Include_Column_List,
INDEX_DATA.is_disabled -- Check if index is disabled before determining which dupe to drop (if applicable)
FROM sys.indexes INDEX_DATA
INNER JOIN sys.tables TABLE_DATA
ON TABLE_DATA.object_id = INDEX_DATA.object_id
INNER JOIN sys.schemas SCHEMA_DATA
ON SCHEMA_DATA.schema_id = TABLE_DATA.schema_id
WHERE TABLE_DATA.is_ms_shipped = 0
AND INDEX_DATA.type_desc IN (''NONCLUSTERED'', ''CLUSTERED'')
)
--Insert all records into a temp table #IndexTemp with appropriate filters:
SELECT * INTO #IndexTemp
FROM CTE_INDEX_DATA DUPE1
WHERE EXISTS
(SELECT * FROM CTE_INDEX_DATA DUPE2
WHERE DUPE1.schema_name = DUPE2.schema_name
AND DUPE1.table_name = DUPE2.table_name
AND DUPE1.key_column_list = DUPE2.key_column_list
AND ISNULL(DUPE1.include_column_list, '') = ISNULL(DUPE2.include_column_list, '')
AND DUPE1.index_name <> DUPE2.index_name)
AND INDEX_NAME NOT LIKE (''%PK%'')
--Return duplicate tbale_names only
SELECT @@SERVERNAME Server_Name, DB_NAME() Database_Name, * from #IndexTemp WHERE table_name IN
(SELECT table_name FROM #IndexTemp GROUP BY table_name HAVING COUNT(*) > 1)
ORDER BY table_name
END
'
Result/error:
Msg 102, Level 15, State 1, Line 2
Incorrect syntax near 'IFmastermaster'.
Msg 18054, Level 16, State 1, Procedure sys.sp_MSforeach_worker, Line 92 [Batch Start Line 0]
Error 55555, severity 16, state 1 was raised, but no message with that error number was found in sys.messages. If error is larger than 50000, make sure the user-defined message is added using sp_addmessage.
2. I'm thinking about using the SQL Agent job on each server to send the email. However, if there is another better strategy then please do let me know as I prefer to send one email per week for the whole environment with Duplicate, Unused, Missing, and
Overlapping Indexes included in one email body.
Thanks in advance for any help