2013-05-01 64 views
4

使用SQL Server 2008的SQL Server性能和索引視圖

(很抱歉,如果這原來是一篇文章,但我想給儘可能多的信息越好。)

我有多個位置其中每個部門都包含多個部門,每個部門都包含多個可以進行零到多次掃描的項目。每次掃描都涉及可能會或可能不會有中斷時間的特定操作。每個項目也屬於一個特定的包,屬於特定的工作,屬於特定的項目,屬於特定的客戶。每個作業都包含一個或多個包含一個或多個項目的包。

         +=============+  +=============+ 
             | Projects | --> | Clients | 
             +=============+  +=============+ 
              ^
               | 
+=============+       +=============+ 
| Locations |       |  Jobs | 
+=============+       +=============+ 
    ^         ^
     |          | 
+=============+  +=============+  +=============+ 
| Departments | <-- | Items | --> | Packages | 
+=============+  +=============+  +=============+ 
         ^
          | 
        +=============+  +=============+ 
        | Scans | --> | Operations | 
        +=============+  +=============+ 

項目表中大約有24,000,000條記錄,掃描表中大約有48,000,000條記錄。新項目偶爾會全天插入到數據庫中,通常數以萬計的流行時間。新掃描每小時插入一次,每個插入的數量從幾百到幾十萬不等。

這些表格被嚴格查詢,切片和切塊每一個方式。我正在編寫非常具體的存儲過程,但它變成了維護的噩夢,因爲我處在一個沒有終點的網站中的一百個存儲過程的邊緣(例如類似於ScansGetDistinctCountByProjectIDByDepartmentIDGroupedByLocationID,ScansGetDistinctCountByPackageIDByDepartmentIDGroupedByLocationID等)。幸運的是,需求幾乎每天都會改變(感覺),每次我必須更改/添加/刪除一列,那麼......我最終在酒吧。

所以我創建了一個索引視圖和一些帶有參數的通用存儲過程,以確定過濾和分組。不幸的是,性能下降了。 我想第一個問題是,因爲選擇性能是最重要的,我應該堅持具體的方法,並通過改變基礎表來對抗嗎?或者,可以做些什麼來加速索引視圖/通用查詢方法?除了緩解維護噩夢之外,我實際上希望索引視圖能夠提高性能。

下面是生成視圖的代碼:

CREATE VIEW [ItemScans] WITH SCHEMABINDING AS 

SELECT 
    p.ClientID   
    , p.ID  AS [ProjectID]    
    , j.ID  AS [JobID] 
    , pkg.ID  AS [PackageID] 
    , i.ID  AS [ItemID]  
    , s.ID  AS [ScanID] 
    , s.DateTime 
    , o.Code 
    , o.Cutoff 
    , d.ID  AS [DepartmentID] 
    , d.LocationID 
    -- other columns 
FROM 
    [Projects] AS p 
    INNER JOIN [Jobs] AS j 
     ON p.ID = j.ProjectID 
    INNER JOIN [Packages] AS pkg 
     ON j.ID = pkg.JobID 
    INNER JOIN [Items] AS i 
     ON pkg.ID = i.PackageID 
    INNER JOIN [Scans] AS s 
     ON i.ID = s.ItemID 
    INNER JOIN [Operations] AS o 
     ON s.OperationID = o.ID 
    INNER JOIN [Departments] AS d 
     ON i.DepartmentID = d.ID; 

和聚簇索引:

CREATE UNIQUE CLUSTERED INDEX [IDX_ItemScans] ON [ItemScans] 
(
    [PackageID] ASC, 
    [ItemID] ASC, 
    [ScanID] ASC 
) 

這裏的通用存儲的特效之一。它獲取已掃描並有一個截止的項目數:

PROCEDURE [ItemsGetFinalizedCount] 
    @FilterBy  int = NULL 
    , @ID   int = NULL 
    , @FilterBy2 int = NULL 
    , @ID2   sql_variant = NULL 
    , @GroupBy  int = NULL   
WITH RECOMPILE 
AS 
BEGIN 

    SELECT 
     CASE @GroupBy   
      WHEN 1 THEN 
       CONVERT(sql_variant, LocationID) 
      WHEN 2 THEN 
       CONVERT(sql_variant, DepartmentID) 
      -- other cases 
     END AS [ID] 
     , COUNT(DISTINCT ItemID) AS [COUNT] 
    FROM 
     [ItemScans] WITH (NOEXPAND) 
    WHERE  
     (@ID IS NULL OR 
     @ID = CASE @FilterBy    
      WHEN 1 THEN   
       ClientID 
      WHEN 2 THEN 
       ProjectID 
      -- other cases 
     END) 
     AND (@ID2 IS NULL OR 
     @ID2 = CASE @FilterBy2   
      WHEN 1 THEN   
       CONVERT(sql_variant, ClientID) 
      WHEN 2 THEN 
       CONVERT(sql_variant, ProjectID) 
      -- other cases 
     END) 
     AND Cutoff IS NOT NULL 
    GROUP BY 
     CASE @GroupBy   
      WHEN 1 THEN 
       CONVERT(sql_variant, LocationID) 
      WHEN 2 THEN 
       CONVERT(sql_variant, DepartmentID) 
      -- other cases 
     END 
END 

我第一次運行查詢,看了看實際的執行計劃,我創建的缺失索引,它建議:

CREATE NONCLUSTERED INDEX [IX_ItemScans_Counts] ON [ItemScans] 
(
    [Cutoff] ASC 
) 
INCLUDE ([ClientID],[ProjectID],[JobID],[ItemID],[SegmentID],[DepartmentID],[LocationID]) 

創建索引會將執行時間縮短到大約五秒鐘,但仍然不可接受(查詢的「特定」版本在亞秒級運行。)我嘗試向索引中添加不同的列,而不是僅將它們包括在內獲得成績(並沒有真正的幫助,我不知道我現在在做什麼。)

下面是查詢計劃:

queryplan

這裏是爲第一指標的細節尋求(它似乎返回所有行的視圖,其中截止IS NOT NULL):

operation

回答

3

的通用PROC可能無法在這種情況下,一個壞主意,但你不必把所有這些案件到最終的查詢,你正在做的事情。我會嘗試建立你的「具體問題」使用動態SQL在通用PROC,幾乎相同的方式蓋爾肖建立一個「包羅萬象」的查詢這裏:

SQL in the Wild - Catch-all queries

這樣,你能緩存查詢計劃並利用博客文章中顯示的索引,並且您應該能夠獲得與之前相同的亞秒級性能。

+0

感謝您的快速響應。我不得不承認,我從來沒有考慮動態SQL。我知道這是一個時間和地點,但我仍然有「動態SQL永遠是邪惡的」在我腦海中迴盪。似乎無法動搖它。 – Frank 2013-05-01 19:20:41

+1

帶參數的'sp_executesql'提供了很多procs的性能,並且可以避免大部分'exec(@sql)'的恐怖。我認爲「必須...使用... PROCS!」的日子很大程度上落後於我們,但不幸的是,我仍然在許多數據庫中保留了數百個數據庫。 – 2013-05-01 19:32:57

+2

你會喜歡這個......如果你看看我的存儲過程,在聲明參數後,我有WITH RECOMPILE。如果我刪除它並在存儲過程的末尾添加OPTION(RECOMPILE),它會飛行。我沒有足夠的智慧去了解它們之間的差異,但是我很高興你發佈了這個鏈接,把我帶到了Erland Sommarskog的網站,我注意到了它的不同之處。另一個原因是,當人們指引我朝着正確的方向而不是告訴我如何做某件事時,我更喜歡它。再次感謝。 – Frank 2013-05-01 20:16:52