Sql-Server

從聚集索引中查找每個項目最早日期的最佳方法是什麼

  • June 14, 2017

我有一個 SQL Server 2012 表,其中包含如下列:

ID int NOT NULL,
EventDate datetime NOT NULL,
... 32 other columns...

該表在大約 10000 個不同的 ID 值的範圍內有大約十億行。

該表有一個唯一的聚集索引,如下所示:

CREATE UNIQUE CLUSTERED INDEX [MyIndex] ON [dbo].[MyTable] (
   [ID] ASC,
   [EventDate] ASC
)

我需要找到最早的 per-ID EventDate,我可以使用以下查詢來獲取它:

SELECT ID, min(EventDate) FROM [dbo].[MyTable] GROUP BY ID

但是,此查詢只需不到 2 分鐘即可完成。

由於 NDA 限制,我無法分享我正在查看的問題的細節(查詢計劃等),但我可以建議我正在查看聚集索引掃描,因此它正在檢查表中的所有行。鑑於數據是按 EventDate 順序組織的,我希望檢索會更快,但我不確定如何。任何其他 ID 特定範圍查詢都會在幾毫秒內響應,並且該表最近已重建和重新索引,因此我認為沒有任何統計更新會有所幫助。

誰能建議一種更好的方法來確定每個 ID 的最小 EventDate 值,從而避免掃描整個聚集索引?

我確實有一個包含(10,000)個不同id值的表。

這與**“索引跳過掃描”優化有關(請參閱下面的連接項,從 2011 年開始)。不幸的是,它已被關閉為“不會修復”**。

一些相關的增強已經存在,但僅適用於分區表:分區表和索引的查詢處理增強

但存在各種解決方法:


解決方法/解決方案 1:CROSS APPLY使用子查詢TOP 1

如果有一個具有 (10K) 個不同ID值的表,我們可以使用它來執行CROSS APPLY.

-- if you don't have a table already
CREATE TABLE MyTableIDs
 ( ID int NOT NULL,
   PRIMARY KEY (id)
 ) ;

-- we only do this once
INSERT INTO MyTableIDs (ID)
SELECT ID 
FROM MyTable
GROUP BY ID ;

然後查詢將對索引執行 (10K) 搜尋:

SELECT  i.ID, a.EventDate
FROM    MyTableIDs AS i
   CROSS APPLY
       ( SELECT TOP (1) t.EventDate
         FROM     MyTable AS t
         WHERE    t.ID = i.ID
         ORDER BY EventDate
       ) AS a ;

解決方法/解決方案 2:使用遞歸 CTE 實施“跳過掃描”

另一種選擇是使用遞歸 CTE 來實現跳過掃描。這是一個展示腳本,由 @PaulWhite 於 2010 年 10 月在 SSC 上發布,展示了 rCTE 可以多快:計算興趣查詢

在你的情況下,它會是這樣的:

WITH    RecursiveCTE
AS      (
       SELECT  a.*
       FROM    ( SELECT TOP (1)  ID, EventDate
                 FROM     MyTable
                 ORDER BY ID, EventDATE
               ) AS a
       UNION   ALL
       SELECT  r.ID, r.EventDate
       FROM    (
               -- A cunning way to use TOP in the recursive part of a CTE ;)
               SELECT  t.ID, t.EventDate,
                       rn = ROW_NUMBER() OVER (ORDER BY t.EventDate)
               FROM    MyTable AS t
               JOIN    RecursiveCTE AS r
                       ON  r.ID < t.ID
               ) AS r
       WHERE   r.rn = 1
       )
SELECT  *
FROM    RecursiveCTE
OPTION  (MAXRECURSION 0) ;

解決方法/解決方案 3:分區表

如果您有企業版,則可用。連結文章中有關此可能性的更多詳細資訊:分區表和索引的查詢處理增強

主要缺點是跳過掃描僅ID在分區列時才有效。


解決方法/解決方案 4:附加索引

添加 NCI 索引(ID, EventDate)允許對更小的索引進行索引掃描。有關解釋,請參閱@Daniel Hutmacher 的答案

它仍然是一個掃描,而不是(很多)搜尋,所以我不確定它是否會像選項 1 和 2 一樣好擴展。當然,一切都取決於細節(列大小、不同 ID 的數量與重複值的數量等) )。


解決方法/解決方案(不,不工作)5:索引視圖

好主意,如果可行的話。GROUP BY不幸的是,如果一個視圖有,則它不能被索引MIN/MAX,請參閱索引視圖的限制。

我想知道為什麼,因為如果它有GROUP BYand是允許的(並被索引) COUNT_BIG()。也可能有一個關於它的 Connect 項目!


解決方法/解決方案 6:“自己動手”索引視圖

自己實現一個等價於索引視圖。例如,您可以在(具有不同值的表,請參閱選項 1)中添加一MinEventDate列,並在該列中添加相應地更新此列的觸發器。那麼您的查詢將是一個簡單的 from 。MyTableIDs``ID``MyTable``SELECT``MyTableIDs


相關網頁的連結

引用自:https://dba.stackexchange.com/questions/176246