2017-07-29 60 views
0

我有一個數百萬行的數據庫,我想創建一個索引來修復性能問題,因爲查詢過度使用處理器。 數據庫託管在MS Azure上。創建一個索引來優化MS SQL查詢

,如果我有類似以下內容的查詢,例如讓員工誰是不是經理:

SELECT name, position, job title WHERE name LIKE '{0}%' AND manager = 0 AND employee = 1 ORDER BY senior DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY 

經理,僱員和資深都是布爾(高級僱員應顯示第一次結果)

我們試圖類似於以下,但服務器仍然無法處理流量的東西:

CREATE NONCLUSTERED INDEX [IX_Index] ON [dbo].[Employees] 
(
    [Manager] ASC, 
    [Employee] ASC, 
    [Name] ASC 
) 
INCLUDE ( [Position], 
    [JobTitle], 
    [Senior]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = ON, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 

執行規劃顯示:

47上索引%尋求和前N個排序的53%,如果我從SSMS搜索

17%索引查找,26%的並行性,56%的排序上跟蹤查詢從web應用

(包括web應用和數據庫託管在Azure上)

什麼是適當的索引?

編輯:

我收到一個建議使用過濾指數(等,其中經理= 0和員工= 1)

但SQL Server將不選擇它,我不能強制它。有什麼建議麼?

+0

請顯示執行計劃。我建議首先放置最具選擇性的列,'[Name] ASC,[Manager] ASC,[Employee] ASC'。 – Serg

+0

@Serg請參閱上面的修改。非常感謝。 – Techy

+0

@Serg另外, 是否最好在開始或內部包含「高級」?因爲我們有它的順序 – Techy

回答

2

你說senior是一個布爾值。這是你唯一要分類的東西。您目前的訂單不夠具體,無法可靠地使用傳呼。您需要二次添加項目,如姓名,使其確定性

爲了優化這個查詢,你可以先添加此指數

CREATE NONCLUSTERED INDEX [IX_Index] 
    ON [dbo].[Employees] ([Senior] ASC, [Name] ASC) 
    INCLUDE ([Position], [JobTitle], [Manager], [Employee]) 
WHERE Manager = 0 AND Employee = 1; 

然後重寫它把它分解成兩個查詢。一個檢索老年人,一個檢索非老年人。

以下使用高效的查找和無排序運算符。

DECLARE @Offset INT = 0, 
     @Fetch INT = 10, 
     @SeniorCount INT; 

DECLARE @Result TABLE (
    ResultId INT IDENTITY PRIMARY KEY, 
    Name varchar(50) NULL, 
    Position varchar(50) NULL, 
    JobTitle varchar(50) NULL 
) 


INSERT INTO @Result 
SELECT TOP (@Fetch + @Offset) name, 
       position, 
       JobTitle 
FROM dbo.Employees 
WHERE name LIKE '{0}%' 
     AND manager = 0 
     AND employee = 1 
     AND Senior = 1 
order by name; 

SET @SeniorCount = @@ROWCOUNT; 


INSERT INTO @Result 
SELECT TOP (@Fetch + @Offset - @SeniorCount) name, 
       position, 
       JobTitle 
FROM dbo.Employees 
WHERE name LIKE '{0}%' 
     AND manager = 0 
     AND employee = 1 
     AND Senior = 0 
order by name 
OPTION (RECOMPILE); 

SELECT * 
FROM @Result 
WHERE ResultId > @Offset AND ResultId<= @Offset + @Fetch 
ORDER BY ResultId; 

enter image description here

+0

非常感謝。儘管我們需要分頁。 – Techy

+0

@Techy您當前的'order by'不夠具體,無法可靠地使用傳呼。您需要添加名稱等輔助項目,以確保其具有確定性。儘管可以使用相同的基本方法。您只需將第一個結果實現爲表變量或臨時表。然後,您可以從中選擇所需的內容並計算第二個查詢所需的內容。 –

+0

@Techy在更新的答案 –

1

你的where子句結構不能很好地適合於索引的使用。這會變得很複雜,所以請耐心等待:

經理和員工是低變異性列。 B樹+索引不會幫助。只有極少數行返回時,B-tree +索引才能正常工作。您的表中的每一行都將成爲員工或經理,因此您可能會搜索每一行。你最高的變異候選人是名字。它應該是第一個在索引中。但是,在這個特定的查詢中,like語句可能會導致優化器跳過索引。如果優化器識別出名稱的前綴是已知的,則它可以使用該索引進行部分搜索。

 

    CREATE NONCLUSTERED INDEX [IX_Index] ON [dbo].[Employees] 
    (
    [Name] ASC, 
    [Manager] ASC, 
    [Senior] DESC 
    ) 
    INCLUDE ([Employee], [Position], [JobTitle]) 

最好的指數結構,像經理和員工的低可變性的數據是位圖,其中SQL Server不此時提供:無論哪種方式,我想如下結構指標。我只在索引中包含Manager和Senior,因爲您正在使用偏移量提取。我假設如果行不是經理,它是一名僱員,所以我只需要索引中的一個。 我不確定如何使用結果。我會猜測你將它們分頁回到用戶界面。在這種情況下,請考慮數據的分佈情況。我數了數名稱的數量在我自己的名字表,並得到如下分佈:

 
First   Percent of 
Letter Count Total 
    A 22911 3.31 
    B 58989 8.5 
    BA 13442 1.94 
    ... 

我的表比你小很多,但我相信我會說什麼規模。您需要減少「找到」的行數。在您的查詢中,確保名稱字符串{0}具有足夠的字符,以便找到的百分比很低,例如總行數的5%。如果{0}爲空,則將其更改爲'A',如果{0}爲'B',則將其更改爲'BA'。如果您真的想在{0}爲空時退回所有員工,那麼我認爲索引不會對您有所幫助。索引用於減少結果集。

+0

非常感謝, 我想知道爲什麼你不希望我包括其他列。還有很多其他的列,表中有超過2200萬行,所以我擔心檢索這些列會增加很多負載並減慢查詢速度。你怎麼看? – Techy

+0

將名稱放在索引中首先是**不是**這個查詢的優化。通過這種安排,它可以在名稱上進行範圍搜索,但仍然需要讀取並放棄不符合「經理」和「僱員」謂詞的行。首先使用這些列,它只需要一個索引區域的範圍查找,其中名稱保證與'manager = 0 AND employee = 1'條件相匹配。 –

+0

這也沒有辦法擺脫排序運算符。 –