Sql-Server
SQL Server 的 DMV 建議的缺失索引數量限制的解決方法
在生產環境中,每個 SQL Server 實例都有超過 250 個數據庫(我們稱之為“OrgDb”)。我目前正在進行的項目旨在將 SQL Server 的 DMV 報告的所有缺失索引發送到遙測,以便對發送到這些 orgDb 的查詢的執行情況進行一些後期分析,並可能進行一些優化。
聽起來很簡單,對吧?然而問題是,DMV 可以在單個 SQL Server 中報告的缺失索引數量的最大限制為 500,而我們預計每個 OrgDb 大約有 20 個缺失索引(總共約 5000 個)。
誰能想到這個限制的解決方法?我最初想到的一個解決方案是刪除 DMV 缺失的索引表:
sys.dm_db_missing_index_details sys.dm_db_missing_index_groups sys.dm_db_missing_index_group
每次更新後,但事實證明這些表無法修改:
Error:Ad hoc updates to system catalogs are not allowed.
您無法重置 DMV,但是您可以通過在 DMV中提到的表上創建一個小的過濾索引然後立即刪除該索引來解決此限制並從 DMV 中刪除行。
例如:
CREATE INDEX IX_temp ON dbo.SomeTable(SomeKey) WHERE SomeKey IS NULL; DROP INDEX dbo.SomeTable.IX_temp;
我創建了一個腳本來自動化這個過程。
IF OBJECT_ID('dbo.RemoveMissingIndexSuggestions') IS NOT NULL DROP PROCEDURE RemoveMissingIndexSuggestions; GO CREATE PROCEDURE dbo.RemoveMissingIndexSuggestions ( @Database SYSNAME = NULL --optional, if NULL, clear all suggestions --if specified, only clear suggestions for that database , @Table SYSNAME = NULL --if not NULL, only clear suggestions for the specified table ) AS BEGIN /* Max Vernon, 2016-04-08 Inspired by work by Joe Sack and Glenn Berry at http://www.sqlskills.com/blogs/joe/clearing-missing-index-suggestions-for-a-single-table/ Creates one index for each table that is mentioned in sys.dm_db_missing_index_details then promply drops that index. The index is created with a WHERE clause that is likely to eliminate all or almost all rows, and therefore will be created quite quickly. */ SET NOCOUNT ON; DECLARE @ObjectName SYSNAME; DECLARE @DatabaseName SYSNAME; DECLARE @CreateStmt NVARCHAR(MAX); DECLARE @DropStmt NVARCHAR(MAX); DECLARE @stmt NVARCHAR(MAX); DECLARE @msg NVARCHAR(2000); DECLARE @vars NVARCHAR(1000); DECLARE @Uniquifier NVARCHAR(48); SET @vars = '@stmt NVARCHAR(MAX)'; DECLARE cur CURSOR LOCAL FORWARD_ONLY STATIC FOR WITH cte AS ( SELECT ObjectName = d.name + '.' + s.name + '.' + o.name , DatabaseName = d.name , CreateStmt = N'CREATE INDEX [IX_temp] ON ' + QUOTENAME(s.name) + N'.' + QUOTENAME(o.name) + N'(' + mid.equality_columns + N') WHERE ' + ( SELECT TOP(1) cols.ColName FROM ( SELECT TOP(1) ColName = QUOTENAME(c.name) + N' IS NULL' FROM sys.columns c INNER JOIN sys.key_constraints kc ON c.object_id = kc.parent_object_id WHERE c.object_id = o.object_id AND kc.type_desc = N'PRIMARY_KEY_CONSTRAINT' UNION ALL SELECT TOP(1) QUOTENAME(c.name) + N' = -2147483648' FROM sys.columns c INNER JOIN sys.types ty ON c.system_type_id = ty.system_type_id WHERE ty.name IN ( N'bigint' , N'binary' , N'hierarchyid' , N'int' , N'uniqueidentifier' , N'varbinary' ) ) cols ) + ';' , DropStmt = N'DROP INDEX ' + QUOTENAME(s.name) + N'.' + QUOTENAME(o.name) + '.[IX_temp];' , rn = ROW_NUMBER() OVER (PARTITION BY mid.object_id ORDER BY mid.index_handle) FROM sys.dm_db_missing_index_details mid INNER JOIN sys.objects o ON mid.object_id = o.object_id INNER JOIN sys.schemas s ON o.schema_id = s.schema_id INNER JOIN sys.databases d ON mid.database_id = d.database_id WHERE o.name NOT LIKE '#%' -- ignore temp tables AND (d.name = @Database OR @Database IS NULL) AND (o.name = @Table OR @Table IS NULL) ) SELECT cte.ObjectName , cte.DatabaseName , cte.CreateStmt , cte.DropStmt FROM cte WHERE rn = 1; OPEN cur; FETCH NEXT FROM cur INTO @ObjectName, @DatabaseName, @CreateStmt, @DropStmt; WHILE @@FETCH_STATUS = 0 BEGIN SET @msg = 'Flushing ' + @ObjectName + ' indexes. '; RAISERROR (@msg, 0, 1) WITH NOWAIT; SET @stmt = 'EXEC ' + QUOTENAME(@DatabaseName) + '.sys.sp_executesql @stmt;' SET @Uniquifier = CONVERT(NVARCHAR(48), NEWID(), 0); SET @CreateStmt = REPLACE(@CreateStmt, '[IX_Temp]', '[IX_Temp_' + @Uniquifier + ']') SET @DropStmt = REPLACE(@DropStmt, '[IX_Temp]', '[IX_Temp_' + @Uniquifier + ']') SET @CreateStmt = 'SET ANSI_NULLS ON; SET ANSI_PADDING ON; SET ANSI_WARNINGS ON; SET ARITHABORT ON; SET CONCAT_NULL_YIELDS_NULL ON; SET NUMERIC_ROUNDABORT OFF; SET QUOTED_IDENTIFIER ON; ' + @CreateStmt + ' '; SET @DropStmt = 'SET ANSI_NULLS ON; SET ANSI_PADDING ON; SET ANSI_WARNINGS ON; SET ARITHABORT ON; SET CONCAT_NULL_YIELDS_NULL ON; SET NUMERIC_ROUNDABORT OFF; SET QUOTED_IDENTIFIER ON; ' + @DropStmt + ' '; RAISERROR (@CreateStmt, 0, 1) WITH NOWAIT; RAISERROR (@DropStmt, 0, 1) WITH NOWAIT; EXEC sp_executesql @stmt, @vars, @stmt = @CreateStmt; EXEC sp_executesql @stmt, @vars, @stmt = @DropStmt; FETCH NEXT FROM cur INTO @ObjectName, @DatabaseName, @CreateStmt, @DropStmt; END CLOSE cur; DEALLOCATE cur; END GO
這可以通過將
@Database
參數設置為數據庫名稱來執行,以消除僅與該數據庫相關的推薦索引,或者不使用參數來消除所有推薦索引。@Table
通過將表的名稱傳遞到參數中,可以選擇將其限制為單個表的建議。EXEC dbo.RemoveMissingIndexSuggestions @Database = 'tempdb', @Table = 'SomeTable';
它最多為每個表創建一個索引。
PRIMARY KEY
索引具有唯一的名稱,並使用單個列構造,如果有表,則優先使用表的。如果表沒有主鍵或至少以下類型的列之一,則此過程將失去表:bigint binary hierarchyid int uniqueidentifier varbinary