2016-04-11 53 views
3

我有一個表[Documents]有以下的列:SQL查詢很慢時,ORDER BY語句添加

Name (string) 
Status (string) 
DateCreated [datetime] 

此表有大約100萬的記錄。所有這三列都有一個索引(每個索引都有一個索引)。

當我運行此查詢:

select top 50 * 
from [Documents] 
where (Name = 'None' OR Name is null OR Name = '') 
    and Status = 'New'; 

執行是非常快(300毫秒)

如果我運行相同的查詢,但與ORDER BY條款,這是很慢(3000毫秒)

select top 50 * 
from [Documents] 
where (Name = 'None' OR Name is null OR Name = '') 
    and Status = 'New' 
order by DateCreated; 

據我所知,它在另一個索引(DateCreated)中搜索,但它應該真的慢得多?如果是這樣,爲什麼?我能做些什麼來加快查詢速度(一個複合索引)?

感謝

BTW:所有索引,包括DateCreated具有非常低碎裂,其實我跑了整編,並沒有改變任何事情。

+0

您是否嘗試過使用索引調整嚮導? – Tarzan

+0

數據庫在Azure中,無法對其運行。 – pmeyer

回答

6

至於爲什麼查詢速度較慢,查詢需要按順序返回行,因此它需要進行排序,或者需要使用索引。

使用帶有CreatedDate前導列的索引,SQL Server可以避免排序。但是,SQL Server還需要訪問底層表中的頁面,以評估是否要返回該行,查看「狀態」和「名稱」列中的值。

如果優化器選擇不使用帶有CreatedDate的索引作爲前導列,那麼它需要首先找到滿足謂詞的所有行,然後執行排序操作以按順序獲取這些行。然後它可以返回排序集合中的前五十行。 (SQL Server不一定需要對整個集合進行排序,但它需要遍歷整個集合,並進行足夠的排序以確保它具有需要返回的「前五十個」。

注意:我懷疑你已經知道這一點,但要澄清:SQL Server在TOP 50之前認可ORDER BY。如果你想要任何50行滿足謂詞,但不一定是具有最低DateCreated值的50行,則可以重構/重寫你的查詢,得到(最多)50行,然後執行那些。


一對夫婦的意見,以改進性能

添加一個綜合指數(如其他答案建議)可能會提供一些改進,例如:

ON Documents (Status, DateCreated, Name) 

SQL Server可能能夠使用該索引來滿足狀態上的相等謂詞,並且還可以在沒有排序操作的情況下以DateCreated順序返回行。 SQL服務器也可能能夠滿足來自索引的對謂詞的謂詞,限制查找到基礎表中的頁面的查找次數,這需要對要返回的行進行查找,以獲得行的「全部」列。


對於SQL Server 2008或更高版本,我會考慮篩選索引...依賴狀態的基數=「新」(即,如果滿足謂詞Status='New'是一個相對較小的子集行表。

CREATE NONCLUSTERED INDEX Documents_FIX 
    ON Documents (Status, DateCreated, Name) 
    WHERE Status = 'New' 

我還要修改查詢指定ORDER BY Status, DateCreated, Name

使order by子句的索引相匹配,它並沒有真正改變各行中返回的順序。


作爲一個更復雜的選擇,我會考慮加入一個持久化計算列,並增加對

ALTER TABLE Documents 
    ADD new_none_date_created AS 
     CASE 
     WHEN Status = 'New' AND COALESCE(Name,'') IN ('','None') THEN DateCreated 
     ELSE NULL 
     END 
    PERSISTED 
    ; 

    CREATE NONCLUSTERED INDEX Documents_FIXP 
    ON Documents (new_none_date_created) 
    WHERE new_none_date_created IS NOT NULL 
    ; 

篩選索引然後查詢可以被改寫:

SELECT TOP 50 * 
    FROM Documents 
    WHERE new_none_date_created IS NOT NULL 
    ORDER BY new_none_date_created 
    ; 

+0

爲什麼「我也會修改查詢來指定ORDER BY狀態,DateCreated,名稱。」我知道將狀態添加到order語句不會改變行的順序,但它將如何改進查詢? – pmeyer

+0

此外,我沒有提到的問題,但我的查詢是動態生成使用Dynamic.Linq,所以添加持續計算列是不是一個真正的選擇....但謝謝.... – pmeyer

+0

@pmeyer:我' d修改'ORDER BY'子句以*精確*匹配索引中列的順序,以增加SQL Server使用索引按順序返回行的機會,避免SORT操作。但是我們真的需要看看EXPLAIN來查看優化器是否使用索引。 (優化器*應該足夠聰明,可以使用索引來滿足順序,即使我們只是'ORDER BY DateCreated'。並且包含Name不會傷害任何內容。對查詢進行更改只是我想測試一下,看看解釋 – spencer7593

0

您需要一個由2列組成的索引:(Name,DateCreated)。索引中的字段順序很重要。因此,將您的索引替換爲名稱與兩列(Name,DateCreated)的新索引。

+0

爲什麼只在Name列上,而在Status列上沒有? – pmeyer

+0

讓我們來思考更多。 你對Status ='New'有多少行有任何期待? 你有任何期望可以使用多少行(Name ='None'OR Name null or Name ='')? 什麼是DateCreate,日期或日期和時間? 表中的列可能如何? –

+0

(Name ='None'OR Name or null OR Name ='')= 468076 Status ='New'= 24559。 – pmeyer

1

如果DateCreated字段表示插入時間到表中,您可以創建一個整數id字段並按該整數字段排序。

+0

這是一個很好的觀點,但是我試圖弄清楚爲什麼我的查詢比試圖修復它慢。我可以將這個相同的查詢應用到另一個日期時間列,我會有同樣的問題... – pmeyer