2013-01-08 186 views
0

我試圖調整下面的存儲過程,因爲它在我們的網站上每分鐘調用30k次。優化TSQL存儲過程

CREATE PROCEDURE [dbo].[mltHttpCallStatus] 

    @SupplierId  AS INTEGER, 
    @CallIsGood  AS BIT, 
    @MaxWorkerThreads AS INT, 
    @MaxIOThreads  AS INT, 
    @AvailWorkerThreads AS INT, 
    @AvailIOThreads  AS INT, 
    @ScriptTypeId  AS INT, 
    @SiteTypeId   AS VARCHAR(50), 
    @ConnectionTime  AS INT, 
    @SiteName  AS VARCHAR(50), 
    @HostName  AS VARCHAR(50) 

AS 

    --DEBUG BEN (Flight details keep failing) 07012008 19:30 
    --Return 
SET NOCOUNT ON 

DECLARE @GoodCalls  AS INT, 
    @BadCalls  AS INT 

SET @BadCalls = 0 
SET @GoodCalls = 0 

IF @CallIsGood = 1 
    SET @GoodCalls = 1 

ELSE 
    SET @BadCalls = 1 

    UPDATE HttpCallStatus_tbl SET 
     GoodCalls  = GoodCalls + @GoodCalls, 
     BadCalls  = BadCalls + @BadCalls, 
     TotalConnectionTime = TotalConnectionTime + @ConnectionTime 
    --WHERE dbo.datepart_fn(DayDate) = dbo.datepart_fn(getDate()) 
    WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, DayDate)) = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) 
     AND DATEPART(HOUR, DayDate) = DATEPART(HOUR, getDate()) 
     AND SupplierId = @SupplierId 
     AND ScriptTypeId = @ScriptTypeId 
     AND SiteTypeId = @SiteTypeId 
     AND SiteName = @SiteName 
     AND HostName = @HostName 

    IF @@ROWCOUNT = 0 

    BEGIN 
     INSERT INTO HttpCallStatus_tbl (DayDate,SupplierId,GoodCalls,BadCalls,ScriptTypeId,SiteTypeId,TotalConnectionTime, 
         MaxWorkerThreads,MaxIOThreads,AvailWorkerThreads,AvailIOThreads,SiteName,HostName) 
      VALUES (CONVERT(DATETIME, getDate(), 103), 
       @SupplierId, 
       @GoodCalls, 
       @BadCalls, 
       @ScriptTypeId, 
       @SiteTypeId, 
       @ConnectionTime, 
       0, 
       0, 
       0, 
       0, 
       @SiteName, 
       @HostName) 
    END 

表結構

Column_name   Type  Length 
    DayDate    datetime 8 
    SupplierId   int  4 
    GoodCalls   int  4 
    BadCalls   int  4 
    ScriptTypeId    int  4 
    SiteTypeId   varchar  50 
    TotalConnectionTime   int  4 
    MaxWorkerThreads   int  4 
    MaxIOThreads    int  4 
    AvailWorkerThreads   int  4 
    AvailIOThreads    int  4 
    SiteName   varchar  50 
    HostName   varchar  50 
    SearchCount   int  4 
    DomainId   int  4 

指標

[PK_HttpCallStatus_tbl] clustered, unique, primary key [DayDate],[SupplierId] 

[IX_HttpCallStatus_tbl] nonclustered [SupplierId] 

[idx_HttpCallStatus_tbl_1] nonclustered  
[SupplierId], [ScriptTypeId], [SiteTypeId], [SiteName], [HostName] 
, [DayDate], [GoodCalls], [BadCalls], [TotalConnectionTime] 

我注意到這些變量是未使用@MaxWorkerThreads,@MaxIOThreads,@AvailWorkerThreads, @AvailIOThreads。也可以使變量@goodcalls & @Badcalls TINYINTS。我認爲這是默認的,所以可以改變爲Getdate()

我的問題是它很難衡量的改進因爲它們都是如此之快,是否值得我進行這些修改,我會看到一個小的收益?

+2

什麼是包括索引表結構? –

+0

您正在使用哪個版本的SQL Server? 2005年,2008年或2012年? –

+0

@馬丁史密斯嗨馬丁,ive現在加了他們..它的900萬行表 –

回答

2

試試這個:

CREATE PROCEDURE [dbo].[mltHttpCallStatus] 

    @SupplierId  AS INTEGER, 
    @CallIsGood  AS BIT, 
    @MaxWorkerThreads AS INT, 
    @MaxIOThreads  AS INT, 
    @AvailWorkerThreads AS INT, 
    @AvailIOThreads  AS INT, 
    @ScriptTypeId  AS INT, 
    @SiteTypeId   AS VARCHAR(50), 
    @ConnectionTime  AS INT, 
    @SiteName  AS VARCHAR(50), 
    @HostName  AS VARCHAR(50) 

AS 

    --DEBUG BEN (Flight details keep failing) 07012008 19:30 
    --Return 
SET NOCOUNT ON 

DECLARE @GoodCalls  AS INT, 
     @BadCalls  AS INT, 
     @StartHour DateTime, 
     @EndHour DateTime 

Select @StartHour = DATEADD(Hour, DATEDIFF(Hour, 0, GETDATE()), 0), 
     @EndHour = DATEADD(Hour, 1 + DATEDIFF(Hour, 0, GETDATE()), 0), 
     @BadCalls = 0, 
     @GoodCalls = 0 

IF @CallIsGood = 1 
    SET @GoodCalls = 1 

ELSE 
    SET @BadCalls = 1 

    UPDATE HttpCallStatus_tbl SET 
     GoodCalls  = GoodCalls + @GoodCalls, 
     BadCalls  = BadCalls + @BadCalls, 
     TotalConnectionTime = TotalConnectionTime + @ConnectionTime 
    --WHERE dbo.datepart_fn(DayDate) = dbo.datepart_fn(getDate()) 
    WHERE DayDate >= @StartHour 
     And DayDate < @EndHour 
     AND SupplierId = @SupplierId 
     AND ScriptTypeId = @ScriptTypeId 
     AND SiteTypeId = @SiteTypeId 
     AND SiteName = @SiteName 
     AND HostName = @HostName 

    IF @@ROWCOUNT = 0 

    BEGIN 
     INSERT INTO HttpCallStatus_tbl (DayDate,SupplierId,GoodCalls,BadCalls,ScriptTypeId,SiteTypeId,TotalConnectionTime, 
         MaxWorkerThreads,MaxIOThreads,AvailWorkerThreads,AvailIOThreads,SiteName,HostName) 
      VALUES (CONVERT(DATETIME, getDate(), 103), 
       @SupplierId, 
       @GoodCalls, 
       @BadCalls, 
       @ScriptTypeId, 
       @SiteTypeId, 
       @ConnectionTime, 
       0, 
       0, 
       0, 
       0, 
       @SiteName, 
       @HostName) 
    END 

請注意,我在詢問之前,計算開始時間和結束時間,然後我把它作爲日期範圍搜索。由於您的主鍵的第一列是DayDate,因此這應該會大大提高性能。

2

我認爲你在錯誤的地方進行了優化,你擔心的是一個行上的轉換插入,而忽略在它之前的where子句中的轉換/函數。看起來你已經將邏輯從標量函數移回到where子句,這將有所幫助,但我會專注於改變它:

WHERE DATEADD(dd, 0, DATEDIFF(dd, 0, DayDate)) = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) 
     AND DATEPART(HOUR, DayDate) = DATEPART(HOUR, getDate()) 

成爲將與索引一起工作的東西。看看sargability http://en.wikipedia.org/wiki/Sargable並找出避免這種情況的方法。這將是真正的收益。

另外,仍然在where子句中,只檢查所有其他條件是否具有相同的數據類型,以防止隱式轉換。

+0

@馬丁史密斯真的,它突出了我,雖然我以爲我會指出。如果沒有數據分佈,我想總是有點猜測問題出在哪裏。 – Meff

+0

是的,它最初是一個簡單的使用日期函數的UDF –

+0

@Meff我刪除了該評論,因爲如果有一個記錄匹配每個小時的5個相等謂詞,那麼在一年的過程中意味着8,760個日期時間如此使'DayDate'sargable的條件當然有幫助。 –

1
  1. 降指數[IX_HttpCallStatus_tbl] nonclustered [SupplierId]它由[idx_HttpCallStatus_tbl_1]覆蓋。
  2. 變化指數[idx_HttpCallStatus_tbl_1] nonclustered [SupplierId], [ScriptTypeId], [SiteTypeId], [SiteName], [HostName], [DayDate], [GoodCalls], [BadCalls], [TotalConnectionTime][SupplierId], [ScriptTypeId], [SiteTypeId], [SiteName], [HostName], [DayDate]include列的索引[GoodCalls], [BadCalls], [TotalConnectionTime]

至於存儲過程的推移,我想補充一點的更新上面:

DECLARE @MinDate DATETIME 
DECLARE @MaxDate DATETIME 
SET @MinDate=DATEADD(HOUR,DATEPART(HOUR, GETDATE()),CONVERT(DATETIME,CONVERT(VARCHAR,GETDATE(),101))) 
SET @MaxDate=DATEADD(HOUR,1,@MinDate) 

和更改前兩行的位置

WHERE DayDate>=MinDate AND DayDate<MaxDate 
+0

感謝您的意見,我會放棄第一個索引,第二個索引實際上是包含的列,它在格式化過程中丟失了,對不起 –

+0

@DamagedGoods - 較窄的索引可能對某些查詢仍然有用。我們中的任何一個人都不可能說你應該在不知道你的查詢工作量的情況下肯定放棄它。您可以在索引使用統計信息上使用各種DMV,以評估它是否首先被使用。 –

+0

@MartinSmith自上次重啓以來,它的讀寫比率非常低,所以我放棄了它 –

1

幾件事情:

以下轉換GETDATE()到 '日/月/年'(無時間信息)用於所有插入

CONVERT(DATETIME, getDate(), 103) 

因此,數據類型可以改變爲DATE而不是DATETIME。與該改變,下面的項可以是更改/刪除:

CONVERT(DATETIME, getDate(), 103) 

Can change to: 

CAST(GETDATE() AS DATE) 

這可以全部一起移除,隨着時間的部分不被保存:

AND DATEPART(HOUR, DayDate) = DATEPART(HOUR, getDate()) 

DATEADD(dd, 0, DATEDIFF(dd, 0, DayDate)) = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) 

可更改爲:

DayDate = CAST(GETDATE() AS DATE) 

而且由於你使用SQL服務器2008+,您可以使用MERGE代替UPDATE/INSERT邏輯

http://technet.microsoft.com/en-us/library/bb510625.aspx

+0

Chris,合併聲明會更快嗎?我試圖替換DayDate = CAST(GETDATE()AS DATE),但它沒有返回任何結果..需要小時部分來選擇正確的記錄數,因爲沒有我得到不同的結果 –

+0

@DamagedGoods - 合併性能可能最容易在批量插入/更新,所以你可能看不到太多的收益。我發現很難相信DayDate除了提供日期信息外沒有其他任何東西,除非您還沒有分享這個難題。 –

+0

好的感謝您的努力,我用一個變量來將getdate代碼從WHERE子句移出,因爲它是一個立即修復。它工作了一個治療..在服務器上的CPU下降從80%下降到10% –