2009-07-09 183 views
6

我有一個關於SQL Server索引的問題。我不是DBA,並且假設對於那些你來說答案是清楚的。我使用SQL Server 2008的SQL Server索引順序(日期時間字段)

我有一個表,它是類似於以下(但有更多列):

CREATE TABLE [dbo].[Results](
    [ResultID] [int] IDENTITY(1,1) NOT NULL, 
    [TypeID] [int] NOT NULL, 
    [ItemID] [int] NOT NULL, 
    [QueryTime] [datetime] NOT NULL, 
    [ResultTypeID] [int] NOT NULL, 
    [QueryDay] AS (datepart(day,[querytime])) PERSISTED, 
    [QueryMonth] AS (datepart(month,[querytime])) PERSISTED, 
    [QueryYear] AS (datepart(year,[querytime])) PERSISTED, 
CONSTRAINT [PK_Results] PRIMARY KEY CLUSTERED 
(
    [ResultID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] 
) ON [PRIMARY] 

的重要領域這裏需要注意的是ResultID,主鍵,和QueryTime結果產生的日期時間。

我也有以下指標(其中包括):

CREATE NONCLUSTERED INDEX [IDX_ResultDate] ON [dbo].[Results] 
(
    [QueryTime] ASC 
) 
INCLUDE ([ResultID], 
[ItemID], 
[TypeID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] 

在一個數據庫,在那裏我有表中的一百萬行,做一個查詢,例如,當使用索引

select top 1 * from results where querytime>'2009-05-01' order by ResultID asc 

在同一個數據庫的另一個實例中,有5千萬行,SQL Server決定不使用索引,而是使用聚集索引掃描,結果是速度非常慢。 (速度取決於日期)。即使我使用查詢提示來使其使用IDX_ResultDate,它仍然有點慢,它的時間花費了94%的時間由ResultID排序。我想通過創建一個帶有ResultID和QueryTime的索引作爲索引中的已排序列,我可以加快查詢速度。

因此,我創建了以下內容:

CREATE NONCLUSTERED INDEX [IDX_ResultDate2] ON [dbo].[Results] 
(
[QueryTime] ASC,  
[ResultID] ASC 
) 
INCLUDE ([ItemID], 
[TypeID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] 
GO 

我認爲它會首先使用排序QueryTime找到匹配的結果,這已經ResultID進行排序。然而,情況並非如此,因爲這個指數並沒有改變現有指標的表現。

我然後嘗試了以下指標:

CREATE NONCLUSTERED INDEX [IDX_ResultDate3] ON [dbo].[Results] 
(
    [ResultID] ASC, 
    [QueryTime] ASC 
) 
INCLUDE ([ItemID], 
[TypeID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY] 
GO 

這一個產生預期的結果。它似乎以不變的時間(幾分之一秒)返回。

但是,我很疑惑爲什麼IDX_ResultDate3工作正常,而IDX_ResultDate2不工作。

我會假設在QueryTime的排序列表中進行二分搜索,然後在結果ID的子列表中查看第一個結果,這是獲取結果的最快方法。 (因此,我最初的排序順序)。

旁邊的問題:我應該創建一個持續的列,其中的QueryTime和索引的日期部分,而不是(我已經有三個持久列,你可以看到上面)?

回答

12

我會假設在 的二進制搜索作爲QueryTime的排序列表後跟 通過偷看第一個結果 ResultIDs子列表是獲取結果中最快的 方式。 (因此我的 初始排序順序)。

這將是確實快,但是你的查詢表達不同的請求:您所要求的與所有查詢的最小ResultId ,經過「2009-05-01」發生的結果。爲了滿足請求,它必須在範圍的起始處('2009-05-01')尋找,從這個位置開始掃描以提取所有的ResultId,對它們進行排序然後返回前1(最小ResultId)。你添加的第二個索引[idx_ResultDate2]也沒有多大幫助。查詢必須執行幾乎完全相同的查找和掃描:結果ID在結果日期中排序爲,因此要查找來自的最高ResultId,所有結果在'2009-05-01'之後,查詢仍然必須掃描索引直到結束。

在最後一個索引[IDX_ResultDate3]上,該查詢是作弊的。它的功能是開始掃描inde並查看QueryTime值,知道在此索引中掃描第一個具有所需範圍(>'2009-05-01')中的QueryTime的結果是一個你想要的(因爲ResultId保證是前1名)。您以純粹的運氣得到結果的「零點幾分」:您在索引的開頭有一個匹配的結果。查詢可能會掃描整個索引並匹配非常差的結果。您可以插入一個QueryTime類似於'2010-01-01'的新結果,然後尋找它,您將看到性能下降,因爲查詢必須掃描整個索引直到結束(仍然比表掃描更快,因爲更窄的索引尺寸)。

我的問題是:你絕對肯定你的查詢必須在ORDER BY ResultID中返回TOP 1嗎?或者你只是隨便挑選了訂單?如果您可以將ORDER BY請求更改爲QueryTime,則任何索引(已更新:將QueryTime作爲最左列)將返回簡單的查找和提取,不掃描並且不進行排序。

+0

非常好的解釋。我現在知道了。我會看看我是否可以重新設計應用程序以使用QueryTime排序。 – 2009-07-09 22:00:45

2

您可以更改聚集索引([QueryTime],[ResultID]),或從

select top 1 * from results where querytime>'2009-05-01' order by ResultID asc 

改變你的查詢

select top 1 <only the columns you actually need> from results where querytime>'2009-05-01' order by ResultID asc 

,包括在[IDX_ResultDate2]

所有這些列
+1

+1正好 - 拍攝包含滿足查詢所需的所有字段的「覆蓋」索引(如果可能的話) – 2009-07-09 18:30:51

+0

是的,已經這樣做了(不是在這裏發佈),而是同類的性能。 – 2009-07-09 22:01:25

4

你有一個範圍一個字段的過濾條件以及ORDER BY另一個字段。

在這種情況下,索引即使是複合索引也不能用於滿足這兩個條件。

當您在(queryTime, resultId)上創建索引時,該索引用於過濾。引擎仍然需要訂購結果集。

當您在(resultId, queryTime)上創建索引時,該索引用於排序。

由於您需要一個TOP 1結果,並且令人滿意的結果恰好在索引的開頭,所以後一種方法表現更好。

如果你的過濾條件是選擇性(即它會返回幾行),並且你需要的第一個結果恰好在索引的末尾,那麼第一個結果會更好。

請參閱本文中我的博客更多的解釋和提示上指數創造一個條件:

+0

不錯的博客文章。 – 2009-07-09 21:57:36

0

我會建議的第一件事是檢查統計對於此表(所有索引)是最新的。

由於您使用不同的數據集得到兩個不同的執行計劃,因此似乎SQL Server在挑選一個執行計劃時出現臭名昭着的「判斷調用」。

我同意Remus的解釋,說明你爲什麼用最後一個索引獲得「神奇」結果。

他的建議也不錯 - 你真的想要通過resultID命令嗎?或者,如果您可以通過queryTime進行排序,那麼執行計劃將能夠使用索引順序作爲結果集的順序(AND將通過索引與掃描進行比較),您將獲得更多的性能。

+0

是的,統計數據是最新的。 (是的,它需要排序...不幸!) – 2009-07-09 22:02:18

0

我不確定我能否回答這個問題,但會指出聚簇索引鍵已經作爲任何其他索引的一部分包含在內,所以它的冗餘包含ResultID作爲您建議的其他任何索引的一部分。

相關問題