缺失索引自動(dòng)創(chuàng)建語(yǔ)句

【編者按】 本文主要介紹使用系統(tǒng) SQL 實(shí)體自動(dòng)創(chuàng)建非聚集(non-clustered)索引。作者為意大利軟件工程師 GhostHost(筆名)贷揽。

本文系 OneAPM 工程師編譯呈現(xiàn)烁巫,以下為正文奢人。

引言

一直以來(lái)闸昨,關(guān)于索引的常見(jiàn)問(wèn)題是:判斷哪部分索引對(duì)保證數(shù)據(jù)庫(kù)的良好性能是必需的铺坞。在本文中窗宦,筆者將提供針對(duì)該問(wèn)題的解決方案咧纠。本文用例中的所有代碼都基于名為 dm_db_missing_index_details 的 SQL Server 系統(tǒng)視圖侈询。

背景

在開(kāi)始安裝前,進(jìn)一步了解 dm_db_missing_index_details 會(huì)更有益處诊霹。

dm_db_missing_index_details 會(huì)返回缺失索引的細(xì)節(jié)信息抢野。在本文中横辆,我們將更關(guān)注以下幾列:

  • index_handle:它是一個(gè)獨(dú)特的跨服務(wù)器標(biāo)識(shí)符,并且標(biāo)志一個(gè)特定的缺失索引竹习。
  • equality_columns:包含用于相等謂詞的所有列
  • inequality_columns:包含用于其他比較的所有列
  • included columns索引中所包含的查詢必要出現(xiàn)列
  • statement: 補(bǔ)充完整索引缺失的表名

實(shí)現(xiàn)

本系統(tǒng)的實(shí)現(xiàn)基于以下三個(gè)實(shí)體:

  1. 一個(gè)可以計(jì)算出待創(chuàng)建索引名稱的簡(jiǎn)單函數(shù)
  2. 一個(gè)用于簡(jiǎn)化 dm_db_missing_index_details的用戶視圖
  3. 一個(gè)為每個(gè)索引創(chuàng)建聲明的進(jìn)程

筆者選擇將這個(gè)系統(tǒng)分為三段進(jìn)程誊抛,但實(shí)際上合并存儲(chǔ)過(guò)程和視圖也是可行的。筆者之所以沒(méi)有選擇后一種做法是因?yàn)橄朐趧?chuàng)建索引之前先從業(yè)務(wù)邏輯檢查一下存在哪些索引整陌。

使用代碼

函數(shù) fn_Index_CreateIndexName
在這個(gè)函數(shù)中拗窃,有三個(gè)輸入?yún)?shù):

    1.  @equality_columns
    2.  @equality_columns
    3.  @index_handlE

該函數(shù)的目的是為每個(gè)期望創(chuàng)建的索引都創(chuàng)建一個(gè)唯一名稱。

因此泌辫,首先拼接@equality_columns@equality_columns兩個(gè)輸入變量随夸,如果拼接后所得結(jié)果超過(guò)120個(gè)字符,那就截取至第120個(gè)字符甥郑。

為什么是120個(gè)字符逃魄?

因?yàn)樵赟QL Server中荤西,命名最大長(zhǎng)度為128個(gè)字符澜搅。這個(gè)函數(shù)在 @index_handlE 名字結(jié)尾添加字段以便保證唯一的索引名。

    CREATE FUNCTION [dbo].[fn_Index_CreateIndexName] (@equality_columns NVARCHAR(4000), _
    @Inequality_columns NVARCHAR(4000), @index_handlE INT) RETURNS VARCHAR(128)
    AS
    BEGIN
    
    DECLARE @IndexName NVARCHAR(255)
    
    SET @IndexName = ISNULL(@equality_columns,@Inequality_columns)
    
    SET @IndexName = LTRIM(REPLACE(@IndexName,'[','_'))
    
    SET @IndexName = RTRIM(REPLACE(@IndexName,']','_'))
    
    SET @IndexName = REPLACE(@IndexName,',','')
    
    SET @IndexName = REPLACE(@IndexName,'_ _','_')
    
    IF LEN(@IndexName) > 120
    BEGIN
    
        SET @IndexName = SUBSTRING(@IndexName,0,120)
    
    END  
    
    SET @IndexName = @IndexName + CAST(@index_handlE AS NVARCHAR(15))
    
    RETURN @IndexName 
    END

視圖 vw_Index_MissingIndex
該視圖基于dm_db_missing_index_details和 sys.databases 表邪锌,并使用fn_Index_CreateIndexName 函數(shù)來(lái)計(jì)算缺失的索引名勉躺。

    CREATE VIEW [dbo].[vw_Index_MissingIndex]
    AS
    
    SELECT  '[' + d.name + ']' as DBName,
            [dbo].[fn_Index_CreateIndexName]_
            (mid.equality_columns,mid.Inequality_columns,mid.index_handlE) AS ID,
            REPLACE(mid.equality_columns,',',' ASC,') AS equality_columns,
            REPLACE(mid.Inequality_columns,',',' ASC,') AS Inequality_columns,
            mid.Included_columns,
            mid.[statement]
    FROM sys.dm_db_missing_index_details mid
    INNER JOIN sys.databases d
    on d.database_id = mid.database_id

存儲(chǔ)過(guò)程 usp_Index_MissingIndexCreationStatements
該存儲(chǔ)過(guò)程基于 vw_Index_MissingIndex,并且輸出索引創(chuàng)建語(yǔ)句觅丰。

    CREATE PROCEDURE [dbo].[usp_Index_MissingIndexCreationStatements]
    AS
    
    DECLARE @IndexCreationPlaceholder_Start  AS NVARCHAR(MAX)
    DECLARE @IndexCreationPlaceholder_End  AS NVARCHAR(MAX)
    
    -- PREPARE PLACEHOLDER
    
    SET @IndexCreationPlaceholder_Start = 'IF NOT EXISTS(SELECT * _
    FROM {2}.sys.indexes WHERE [name] = ''IX_{0}'' )
                    BEGIN
                    CREATE NONCLUSTERED INDEX [IX_{0}] ON {1}'
    
    SET @IndexCreationPlaceholder_End = ' WITH (PAD_INDEX = OFF, _
    STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, _
    ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
                    END;' + char(13) + char(10)
    
    -- STATEMENT CREATION
    
    SELECT
        DBName,
        CASE
        WHEN NOT mid.equality_columns IS NULL AND NOT mid.Inequality_columns IS NULL THEN
                    REPLACE(REPLACE(REPLACE(@IndexCreationPlaceholder_Start,'{0}', _
                    mid.ID),'{1}',mid.[statement]),'{2}',mid.DBName)
                    + '
                       ( ' +
                       COALESCE(mid.equality_columns,'') +
                       ' ASC,' + 
                       COALESCE(mid.Inequality_columns,'') +
                       ' ASC
                    )' +
                    COALESCE('INCLUDE ( ' + mid.Included_columns + ' ) ','')
                    + @IndexCreationPlaceholder_End
    
            WHEN mid.equality_columns IS NULL AND NOT mid.Inequality_columns IS NULL THEN
                    REPLACE(REPLACE(REPLACE(@IndexCreationPlaceholder_Start,_
                    '{0}', mid.ID),'{1}',mid.[statement]),'{2}',mid.DBName)
                    + '
                       ( ' +
                       COALESCE(mid.Inequality_columns,'') +
                       ' ASC
                    ) ' +
                    COALESCE('INCLUDE ( ' + mid.Included_columns + ' ) ','')
                    + @IndexCreationPlaceholder_End
    
        WHEN NOT mid.equality_columns IS NULL AND mid.Inequality_columns IS NULL THEN
                REPLACE(REPLACE(REPLACE(@IndexCreationPlaceholder_Start,'{0}', _
                mid.ID),'{1}',mid.[statement]),'{2}',mid.DBName)
                + '
                   ( ' +
               COALESCE(mid.equality_columns,'') +  ' ASC
                    ) ' +
                COALESCE('INCLUDE ( ' + mid.Included_columns + ' ) ','')
                + @IndexCreationPlaceholder_End
        ELSE NULL
    END AS Index_Creation_Statement,
        ' DROP INDEX [IX_' + mid.ID  + '] ON ' + mid.[statement]  _
        +  + char(13) + char(10) AS Index_Drop_Statement
    FROM [dbo].[vw_Index_MissingIndex] AS mid

完整代碼

    -- CREATE FUNCTION fn_Index_CreateIndexName
    
    CREATE FUNCTION [dbo].[fn_Index_CreateIndexName] (@equality_columns NVARCHAR(4000), _
    @Inequality_columns NVARCHAR(4000), @index_handlE INT) RETURNS VARCHAR(128)
    AS
    BEGIN
    
            DECLAR
    
    E @IndexName NVARCHAR(MAX)
    
        SET @IndexName = ISNULL(@equali
    
    ty_columns,@Inequality_columns)
    
    SET @IndexName = LTRIM(REPLACE(@IndexName,'[','_'))
    
    SET @IndexName = RTRIM(REPLACE(@IndexName,']','_'))
    
    SET @IndexName = REPLACE(@IndexName,',','')
    
    SET @IndexName = REPLACE(@IndexName,'_ _','_')
    
        IF LEN(@IndexName) > 120
        BEGIN
    
            SET @IndexName = SUBSTRING(@IndexName,0,120)
    
        END  
    
        SET @IndexName = @IndexName + CAST(@index_handlE AS NVARCHAR(15))
    
        RETURN @IndexName 
    END
    
    GO
    
    -- CREATE FUNCTION vw_Index_MissingIndex
    
    CREATE VIEW [dbo].[vw_Index_MissingIndex] 
    AS
    
    SELECT  '[' + d.name + ']' as DBName,
            [dbo].[fn_Index_CreateIndexName]_
            (mid.equality_columns,mid.Inequality_columns,mid.index_handlE) AS ID,
            REPLACE(mid.equality_columns,',',' ASC,') AS equality_columns,
            REPLACE(mid.Inequality_columns,',',' ASC,') AS Inequality_columns,
            mid.Included_columns,
            mid.[statement]
    FROM sys.dm_db_missing_index_details mid
    INNER JOIN sys.databases d
    on d.database_id = mid.database_id
    
    GO
    
    CREATE PROCEDURE [dbo].[usp_Index_MissingIndexCreationStatements]
    AS
    
    DECLARE @IndexCreationPlaceholder_Start  AS NVARCHAR(MAX)
    DECLARE @IndexCreationPlaceholder_End  AS NVARCHAR(MAX)
    
    -- PREPARE PLACEHOLDER
    
    SET @IndexCreationPlaceholder_Start = 'IF NOT EXISTS_
    (SELECT * FROM {2}.sys.indexes WHERE [name] = ''IX_{0}'' )
                    BEGIN
                    CREATE NONCLUSTERED INDEX [IX_{0}] ON {1}'
    
    SET @IndexCreationPlaceholder_End = ' WITH (PAD_INDEX = OFF, _
    STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, _
    ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
                    END;' + char(13) + char(10)
    
    -- STATEMENT CREATION
    
    SELECT
        DBName,
        CASE
        WHEN NOT mid.equality_columns IS NULL AND NOT mid.Inequality_columns IS NULL THEN
                    REPLACE(REPLACE(REPLACE(@IndexCreationPlaceholder_Start,'{0}', _
                    mid.ID),'{1}',mid.[statement]),'{2}',mid.DBName)
                    + '
                       ( ' +
                       COALESCE(mid.equality_columns,'') +
                       ' ASC,' + 
                       COALESCE(mid.Inequality_columns,'') +
                       ' ASC
                    )' +
                    COALESCE('INCLUDE ( ' + mid.Included_columns + ' ) ','')
                    + @IndexCreationPlaceholder_End
    
            WHEN mid.equality_columns IS NULL AND NOT mid.Inequality_columns IS NULL THEN
                    REPLACE(REPLACE(REPLACE(@IndexCreationPlaceholder_Start,'{0}', _
    
    
    mid.ID),'{1}',mid.[statement]),'{2}',mid.DBName)
                + '
                   ( ' +
                   COALESCE(mid.Inequality_columns,'') +
                   ' ASC
                ) ' +
                COALESCE('INCLUDE ( ' + mid.Included_columns + ' ) ','')
                + @IndexCreationPlaceholder_End
    
        WHEN NOT mid.equality_columns IS NULL AND mid.Inequality_columns IS NULL THEN
                REPLACE(REPLACE(REPLACE(@IndexCreationPlaceholder_Start,'{0}', _
                mid.ID),'{1}',mid.[statement]),'{2}',mid.DBName)
                + '
                   ( ' +
               COALESCE(mid.equality_columns,'') +  ' ASC
                    ) ' +
                COALESCE('INCLUDE ( ' + mid.Included_columns + ' ) ','')
                + @IndexCreationPlaceholder_End
        ELSE NULL
    END AS Index_Creation_Statement,
    ' DROP INDEX [IX_' + mid.ID  + '] ON ' + mid.[statement]  _
        +  + char(13) + char(10) AS Index_Drop_Statement
    FROM [dbo].[vw_Index_MissingIndex] AS mid
    
    GO

OneAPM 助您輕松鎖定 .NET 應(yīng)用性能瓶頸饵溅,通過(guò)強(qiáng)大的 Trace 記錄逐層分析,直至鎖定行級(jí)問(wèn)題代碼妇萄。以用戶角度展示系統(tǒng)響應(yīng)速度蜕企,以地域和瀏覽器維度統(tǒng)計(jì)用戶使用情況。想閱讀更多技術(shù)文章冠句,請(qǐng)?jiān)L問(wèn) OneAPM 官方博客轻掩。

本文轉(zhuǎn)自 OneAPM 官方博客

原文地址:http://www.codeproject.com/Tips/1079651/Automatic-Missing-Non-Clustered-Creation-Statement

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市懦底,隨后出現(xiàn)的幾起案子唇牧,更是在濱河造成了極大的恐慌,老刑警劉巖聚唐,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丐重,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡杆查,警方通過(guò)查閱死者的電腦和手機(jī)扮惦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)亲桦,“玉大人崖蜜,你說(shuō)我怎么就攤上這事掺栅。” “怎么了纳猪?”我有些...
    開(kāi)封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵氧卧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我氏堤,道長(zhǎng)沙绝,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任鼠锈,我火速辦了婚禮闪檬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘购笆。我一直安慰自己粗悯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布同欠。 她就那樣靜靜地躺著样傍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铺遂。 梳的紋絲不亂的頭發(fā)上衫哥,一...
    開(kāi)封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音襟锐,去河邊找鬼撤逢。 笑死,一個(gè)胖子當(dāng)著我的面吹牛粮坞,可吹牛的內(nèi)容都是我干的蚊荣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼莫杈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼互例!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起姓迅,我...
    開(kāi)封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤敲霍,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后丁存,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體肩杈,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年解寝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扩然。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡聋伦,死狀恐怖夫偶,靈堂內(nèi)的尸體忽然破棺而出界睁,到底是詐尸還是另有隱情,我是刑警寧澤兵拢,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布翻斟,位于F島的核電站,受9級(jí)特大地震影響说铃,放射性物質(zhì)發(fā)生泄漏访惜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一腻扇、第九天 我趴在偏房一處隱蔽的房頂上張望债热。 院中可真熱鬧,春花似錦幼苛、人聲如沸窒篱。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)墙杯。三九已至,卻和暖如春暑椰,著一層夾襖步出監(jiān)牢的瞬間霍转,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工一汽, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人低滩。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓召夹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親恕沫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子监憎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容