Index

如果 DMVS 中沒有統計資訊,可以刪除 FK 上的索引

  • May 8, 2020

我使用 Kevin Kline 下面的眾所周知的查詢來檢查未使用的索引。在外鍵上創建的幾個索引不返回讀取統計資訊,只返回寫入。

您是否 100% 安全地刪除這些索引?或者優化器是否可以將它們用於插入或刪除,並且不會在 DMV 中註冊任何統計資訊?如果是這樣,如何判斷它們是 100% 安全移除的?

我已經讓伺服器執行了 2 個月,所以我確信我已經涵蓋了我們每月的工作負載週期。

   SELECT o.name
   , indexname=i.name
   , i.index_id   
   , reads=user_seeks + user_scans + user_lookups   
   , writes =  user_updates   
   , rows = (SELECT SUM(p.rows) FROM sys.partitions p WHERE p.index_id = s.index_id AND s.object_id = p.object_id)
   , CASE
       WHEN s.user_updates < 1 THEN 100
       ELSE 1.00 * (s.user_seeks + s.user_scans + s.user_lookups) / s.user_updates
     END AS reads_per_write
   , 'DROP INDEX ' + QUOTENAME(i.name) + ' ON ' + QUOTENAME(c.name) + '.' + QUOTENAME(OBJECT_NAME(s.object_id)) as 'drop statement'
   FROM sys.dm_db_index_usage_stats s  
   INNER JOIN sys.indexes i ON i.index_id = s.index_id AND s.object_id = i.object_id   
   INNER JOIN sys.objects o on s.object_id = o.object_id
   INNER JOIN sys.schemas c on o.schema_id = c.schema_id
   WHERE OBJECTPROPERTY(s.object_id,'IsUserTable') = 1
   AND s.database_id = DB_ID()   
   AND i.type_desc = 'nonclustered'
   AND i.is_primary_key = 0
   AND i.is_unique_constraint = 0
   AND (SELECT SUM(p.rows) FROM sys.partitions p WHERE p.index_id = s.index_id AND s.object_id = p.object_id) > 2000
   ORDER BY  name, reads

只要您確定伺服器在那段時間內一直處於執行狀態,並且沒有人無意中清除了 DMV 統計資訊。如果數據庫分離+重新連接/恢復/離線+線上/自動關閉+線上,或者如果索引已被顯式刪除/重新創建(DMV不受禁用/重建/重組的影響,除了 SQL Server 2012 的情況,重建目前會清除統計資訊- 我懷疑這將得到修復 - 感謝@MartinSmith)。

您應該預料到,如果 DML 活動出於某種原因(例如,對某個其他表執行更新)在讀取容量中使用索引,這將註冊為讀取活動,而不是寫入活動。您看到的所有寫入都是索引維護。

順便說一句,這是編寫此查詢的一種更有效的方法。sys.objects我刪除了兩次引用的相關子查詢,並刪除了對and的不必要的連接sys.schemas。我還修復了一些小的語法問題,例如在適當的情況下為所有列添加別名,將保留字放在方括號中,以及刪除AS 'column alias'語法。SELECT我發現在您從alias = expression語法更改為語法的列表中途感到困惑expression AS alias- 您應該選擇一個並保持一致恕我直言。我在查詢中更改了大多數,也根據我的個人喜好更改了這些。:-)

SELECT name = OBJECT_NAME(s.[object_id])
   , indexname = i.name
   , i.index_id   
   , reads = s.user_seeks + s.user_scans + s.user_lookups   
   , writes = s.user_updates   
   , g.[rows]
   , reads_per_write = CASE
       WHEN s.user_updates < 1 THEN 100.0
       ELSE 1.00 * (s.user_seeks + s.user_scans + s.user_lookups) / s.user_updates
     END
   , [drop statement] = 'DROP INDEX ' + QUOTENAME(i.name) 
     + ' ON ' + QUOTENAME(OBJECT_SCHEMA_NAME(s.[object_id])) 
     + '.'    + QUOTENAME(OBJECT_NAME(s.[object_id]))
   FROM sys.dm_db_index_usage_stats AS s  
   INNER JOIN sys.indexes AS i 
     ON i.index_id = s.index_id 
     AND s.object_id = i.object_id   
   INNER JOIN 
   (
     SELECT [object_id], index_id, [rows] = SUM([rows]) 
       FROM sys.partitions GROUP BY [object_id], index_id
       HAVING SUM([rows]) > 2000
   ) AS g
     ON i.[object_id] = g.[object_id] 
     AND i.index_id = g.index_id
   WHERE OBJECTPROPERTY(s.[object_id], 'IsUserTable') = 1
   AND s.database_id = DB_ID()   
   AND i.type_desc = 'nonclustered'
   AND i.is_primary_key = 0
   AND i.is_unique_constraint = 0
   ORDER BY name, reads;

引用自:https://dba.stackexchange.com/questions/31635