2015-05-15 28 views
5

綜合指數如何在有效的過期表格上工作?TSQL中具有多個維度的2類有效指定指數表現

利用T-SQL,讓我們說我有一個表,它是有效的與產品相關的EffectiveStartDate和effectiveEndDate爲日記錄的歷史價格波動的影響,所以我的表將採取以下形式:

MyTable的:=(EffStartDate日期,EffEndDate日期,ProductID int,ProductPrice貨幣) 其中EffEndDate = '12/31/9999'當記錄有效時。

讓我們進一步假設我實現了這個表形式的兩個指標: 聚集在(EffEndDate,EffStartDate,產品ID) 非聚集上(EffEndDate,產品ID)

從我的理解,索引創建爲聚集索引將信息存儲在由索引創建語句的列指定順序排序的B樹(潛在地爲B +)中。因此,我設想了EffEndDate,EffStartDate和ProductID的表格排序。大多數情況下,我希望通過類似下面的查詢歷史查詢: 從MyTable 中選擇* ,其中ProductID = @ProductID 和EffStartDate和EffEndDate之間的@MyDate。

我試圖想象如何B型樹實際存儲這三列相關的信息。是否將它作爲一個元組對象進行存儲,就像您可以在Python中找到的一樣,或者在索引是複合時將更多維度添加到B樹中?例如,對於給定的EffEndDate,B樹是否有多個與EffStartDates相關的分裂樹,然後是多個與ProductID相關的分裂樹,或者每個分裂樹都基於一個元組進行分割?這個迴應似乎認爲它採用元組方法: Question

如果採用單維方法,我很難概念化這些類型的指數如何爲兩列之間的日期範圍查找提供整體值。例如,我發現它發生了這樣的情況,給定日期(@MyDate),我們可以使用索引的EffEndDate組件將搜索限制爲僅EffEndDates> = @MyDate,然後使用EffStartDate組件將搜索限制爲只有EffStartDate < = @MyDate,然後搜索此剩餘範圍內的ProductID。這是如何使用索引?

我預見到的問題是,如果我們有大約10萬個產品每週都會非統一更新,那麼我們最終會利用這個聚集索引來生成一組巨大的日期範圍,然後會有爲每個日期範圍搜索我們想要的ProductID的實例。有沒有更好的索引來實現這種類型的查詢?

我相信非聚集索引的存在是爲了快速搜索出當前的ProductID價格,因爲我們只需要兩塊拼圖,因爲EffEndDate會被設置爲'12/31/9999'。

另外,是否有一種方法來實現跨多列的多維索引來提高T-SQL中的查詢性能?

謝謝!

回答

3

這是一個真正需要2D或空間索引的應用程序,因爲您正在有效地將兩個單獨的不等式搜索組合在一起。如果沒有將表格堵塞到可以使用SQL Server空間索引的表單中,那麼您的選項是有限的。

如果可能,最好的方法是找到EffStartDate和EffEndDate之間的某種業務關係。例如,如果存在這些值不能比一年更遠的規則,那麼這可以編入您的WHERE子句中,以便爲索引提供額外的選擇性,否則您可能需要進行大量掃描。

喜歡的東西:

SELECT * 
FROM Table 
WHERE @date BETWEEN EffStartDate and EffEndDate 
    AND DATEADD(year, -1, @date) < EffStartDate 

...你在哪裏添加額外的業務限制,以減少搜索空間的查詢需要遍歷。

,可能是你的興趣

兩篇文章是這些:

Quassnoi's answer to a similar question,其中談到如何強制適合這種類型的數據轉換成可以在空間索引的格式,並且還擁有一個鏈接到他的該博客詳細介紹了一種遞歸CTE方法,可用於加速這些類型的查詢而無需修改架構。

Michael Asher's article關於使用業務知識來提高相似類型查詢的性能。

+0

感謝您提供這些鏈接並突出顯示B-tree無法將其剪切爲空間連接。我將嘗試實施一個測試解決方案,將日期範圍強制爲空間兼容的數據類型,然後將性能與其他用戶建議的B樹索引進行比較。我選擇這個作爲最有用的答案,因爲它突出了與更有效的複合一維實現相反的多維方法。謝謝! – PkmnBugCatcher

1

有表中沒有的LoanID
我假設你的意思的ProductID

如果你要搜索的產品id = @ProductID那麼爲什麼在世界上,你會埋葬,作爲一個綜合指數的尾部。你爲什麼最後做簡單的事情?

100K一週更新不算什麼。你正在想這個。只需在每列上放置一個索引,然後讓查詢優化器完成它的工作。

如果您設置了複合索引,則ProductID,EffStartDate日期,EffEndDate日期。
你不會比索引尋找更好!
enter image description here

+0

是的,我的意思是ProductID - 謝謝。我有多個我正在使用的示例表,顯然無法讓它們保持直線。 – PkmnBugCatcher

+0

沒有意識到shift + enter是一條新線。以下是我的想法: 我最初的想法是關於爲什麼要將ProductID埋在組合索引結尾的原因與您的想法相同,但這是我最近在野外看到的一個實現。這讓我想到後臺的B樹複合索引如何存儲和訪問數據,而且我無法理解(EffEndDate,EffStartDate,ProductID)複合訂單的優點促使我發佈此問題。謝謝! – PkmnBugCatcher

+0

@Blam,哦,我看到你已經發布了'index seek'運算符細節的截圖。你可以看到有'Seek predicate'和'Predicate'。 'Seek謂詞'用於在索引樹中進行快速搜索。 「謂詞」是對每個找到的行所需的任何其他額外條件的評估。因此,如果'seek predicate'中的條件('fieldID = 117 AND value>'1901-01-01')導致NNN行,則'Predicate'中的條件將被評估爲NNN次。將'value2'作爲索引的一部分不會減少處理的這一行數量。 –

1

模擬真實數據。生成大型表格(最終表格的大小應與您在現實生活中期望的大小相同),並按照您在現實生活中的預期分配產品和日期。首先在產品上添加三個獨立的索引,開始日期,結束日期。嘗試運行查詢。分析執行計劃。嘗試其他索引組合。比較計劃和表現。如果沒有東西給出可接受的性能,請使用生成示例數據和查詢的腳本返回。

在我的測試中,優化器是三個獨立索引搜索的內部連接結果。

創建表

加三爲每列索引無關:

CREATE TABLE [dbo].[Test](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [ProductID] [int] NOT NULL, 
    [StartDate] [date] NOT NULL, 
    [EndDate] [date] NOT NULL, 
CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
(
    [ID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

CREATE NONCLUSTERED INDEX [IX_EndDate] ON [dbo].[Test] 
(
    [EndDate] ASC 
)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) ON [PRIMARY] 

CREATE NONCLUSTERED INDEX [IX_ProductID] ON [dbo].[Test] 
(
    [ProductID] ASC 
)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) ON [PRIMARY] 

CREATE NONCLUSTERED INDEX [IX_StartDate] ON [dbo].[Test] 
(
    [StartDate] ASC 
)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) ON [PRIMARY] 

生成測試數據

  • 1M行。
  • 多達100個具有統一分佈的不同產品ID。
  • 開始日期內萬天從2000-01-01(〜27年的時間跨度)
  • 結束日期爲達到1000天,從開始日期(病程可達〜3年)

查詢:

INSERT INTO Test(ProductID, StartDate, EndDate) 
SELECT TOP(1000000) 
    CA.ProductID 
    ,DATEADD(day, StartOffset, '2000-01-01') AS StartDate 
    ,DATEADD(day, StartOffset+DurationDays, '2000-01-01') AS EndDate 
FROM 
sys.all_objects AS o1 
cross join sys.all_objects AS o2 
cross apply 
(
    SELECT 
     cast((cast(CRYPT_GEN_RANDOM(4) as int)/4294967295.0 + 0.5) * 100 + 1 as int) AS ProductID 
     ,cast((cast(CRYPT_GEN_RANDOM(4) as int)/4294967295.0 + 0.5) * 10000 as int) AS StartOffset 
     ,cast((cast(CRYPT_GEN_RANDOM(4) as int)/4294967295.0 + 0.5) * 1000 as int) AS DurationDays 
) AS CA 

查詢優化:

DECLARE @VarDate date = '2004-01-01'; 
SELECT * 
FROM Test 
WHERE 
    ProductID = 1 
    AND @VarDate >= StartDate 
    AND @VarDate <= EndDate 
; 

它返回〜500行。

執行計劃

plan

服務器建議下列指數:

CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>] 
ON [dbo].[Test] ([ProductID],[StartDate],[EndDate]) 
INCLUDE ([ID]) 

但具有這樣的指數是愚蠢,IMHO。

如果總共有1M行和100K個不同的產品ID,而不是100;換句話說,如果由特定產品ID搜索消除絕大部分的行,那麼最好的選擇是有可能對產品id一個索引,包括其他列進去:

CREATE NONCLUSTERED INDEX IX_Product 
ON [dbo].[Test] ([ProductID]) 
INCLUDE ([StartDate],[EndDate]) 

OR

CREATE NONCLUSTERED INDEX IX_Product 
ON [dbo].[Test] ([ProductID], [StartDate]) 
INCLUDE ([EndDate]) 

CREATE NONCLUSTERED INDEX IX_Product 
ON [dbo].[Test] ([ProductID],[EndDate]) 
INCLUDE ([StartDate]) 

如果其中一個日期提供了很好的選擇性,那麼在它上面有一個索引而不是ProductID。

如果沒有色譜柱具有良好的選擇性,那麼它很難。

編輯

這是愚蠢的,盲目地做出一個索引優化器的建議,因爲你知道你將搜索特定的產品ID,但隨後的一系列StartDates,然後EndDates的範圍。所以,第三列EndDate永遠不會用於搜索本身。在這種情況下,索引中的這一列最好爲INCLUDE,而不是如上所示將其作爲索引的一部分。

如果查詢的是特定的ProductID和特定的StartDate(不是範圍),那麼對於一個範圍的結束日期(或特定結束日期),然後將具有結束日期作爲指標,將有助於的一部分。

+0

所有這些工作和你的結論是建議的指數是愚蠢的。這不是愚蠢的。 – Paparazzi

+0

@Blam,我已經將我的解釋添加到了爲什麼優化器建議的索引不是一個好主意的答案中。 –

+0

謝謝你的後續解釋併產生這個例子。我認爲首先在產品ID上添加索引顯然是一種可行的方式,因爲它顯着限制了搜索空間,我將不得不考慮組合鍵與包含列方法的有效性。我可以使用您的仿真將數據碰撞到大約700K ProductID,時間跨度超過五年,然後比較兩者之間的性能。你的回答非常有幫助,謝謝! – PkmnBugCatcher

相關問題