2015-08-28 125 views
1

我在表的更新觸發器中有一個查詢(見下文),並根據觸發器中當前更新的內容更新大數據集上的某些字段。For一套10萬點的記錄它正在約22秒,我想優化這更優化SQL Server 2008中大數據集的更新查詢

update RED 
set EDR_IsLock = @il 
where RED.docid in 
     (select a.DocID 
     from 
      (select distinct DocID 
      from UVD 
      where UVD.UVID in (select i.UVID from inserted i)) a 
     left outer join 
      (select distinct UVD.DocID 
      from UVD 
      inner join UVH on UVD.UVID = UVH.UVID 
      where UVD.UVID not in (select i.UVID from inserted i) 
       and UVH.IsLock = 1) b on a.DocID = b.DocID 
     where b.DocID is null) 

PS:表紅包含超過記錄

+0

這是觸發器嗎? – lad2025

+1

你試過在SSMS中執行它嗎?您可以嘗試並檢查執行計劃以獲取提示。 – Paolo

+0

@ lad2025是的,這是在觸發 –

回答

3

一萬億嘗試加入,而不是條款。嘗試下面的一個,它可能會幫助你。

Update r 
Set  EDR_IsLock = @il 
From RED As r 
     Join 
     (
      Select a.DocID 
      From ( Select Distinct DocID 
         From UVD 
           Join inserted i On UVD.UVID = i.UVID 
        ) a 
        left outer join 
        (
         Select Distinct 
           UVD.DocID 
         From UVD 
           join UVH on UVD.UVID = UVH.UVID 
         Where UVD.UVID Not In (Select i.UVID From inserted i) 
           And UVH.IsLock = 1 
        ) b on a.DocID = b.DocID 
      Where b.DocID is null 
     ) As t On r.docid = t.DocID 

更新:

我不是一個很好的解釋器,但是,如果「不存在」做出的,而不是「在條款」顯着性差異,那麼我會重寫上述查詢,如下所示:

Set Nocount On; 

Declare @UVD Table 
(
    DocID   Int 
) 

Declare @UVDWithUVH Table 
(
    DocID   Int 
) 

Insert Into @UVD(DocID) 
Select Distinct 
     DocID 
From UVD As u With (Nolock) 
     Join inserted i On u.UVID = i.UVID 

Insert Into @UVDWithUVH(DocID) 
Select Distinct 
     u.DocID 
From UVD As u With (Nolock) 
     Join UVH As uh With (Nolock) on u.UVID = uh.UVID 
Where Not Exists (Select 1 From inserted As i Where i.UVID = u.UVID) 
     And uh.IsLock = 1 

Update r 
Set  EDR_IsLock = @il 
From RED As r 
     Join 
     (
      Select a.DocID 
      From @UVD As a 
        Left Outer Join @UVDWithUVH As b On a.DocID = b.DocID 
      Where b.DocID Is Null 
     ) As t On r.docid = t.DocID 

在這個解決方案中,我建議使用@Table變量,它將駐留在RAM中而不是物理存儲器中。在Join時刪除查詢內部查詢的開銷。

嘗試此更新之一,並想知道,它是否有助於您提高觸發器的性能。

+0

爲什麼地球上你會使用'NOLOCK'觸發器?!另外,表變量存儲在RAM中的語句不是真的,它就像臨時表一樣存儲在臨時數據庫中。有關表變量及其行爲的更多信息,請參閱[本答案](http://dba.stackexchange.com/a/16386/7257)。 – GarethD

+0

謝謝,你糾正我的表變量存儲位置。而對於NOLOCK,在這裏,NOLOCK僅適用於那些僅用於比較的表格。因此,如果原始數據來自NOLOCK進行比較,那麼沒有任何危險。同意,如果有像銀行應用程序這樣的交易關鍵應用程序,則不應使用NOLOCK。糾正我,如果我也是錯誤的NOLOCK。 –

+0

問題總是,更糟的是,數據或鎖定不正確。在大多數情況下,我會說不正確的數據更糟,特別是考慮到查詢的結果將形成更新的基礎。使用'NOLOCK'不僅會給讀取未提交記錄的機會,還可能導致記錄被完全遺漏,或者讀取兩次,或者讀取一個從未存在的記錄。有關更多閱讀和不需要的副作用的演示,請參閱Paul White的文章[讀取未提交的隔離級別](http://sqlperformance.com/2015/04/t-sql-queries/the-read-uncommitted-isolation-level )。 – GarethD

3

我會親自改寫這個爲:

UPDATE RED 
SET  EDR_IsLock = @il 
WHERE RED.DocID IN 
     ( SELECT UVD.DocID 
      FROM UVD 
      WHERE RED.DocID = UVD.DocID 
      AND  NOT EXISTS (SELECT 1 FROM inserted i WHERE i.UVID = UVD.UVID) 
      AND  NOT EXISTS 
        ( SELECT UVD.DocID 
         FROM UVD AS UVD2 
         WHERE UVD2.DocID = UVD.DocID 
         AND  EXISTS (SELECT 1 FROM UVH WHERE UVH.UVID = UVD2.UVID AND UVH.IsLock = 1) 
         AND  NOT EXISTS (SELECT 1 FROM inserted i WHERE i.UVID = UVD2.UVID) 
        ) 
     ); 

在我更換JOIN所有實例,INEXISTS。 SQL Server在使用LEFT JOIN/IS NULL刪除記錄時出現問題,如documented here,在最佳情況下,您將獲得與NOT EXISTS相同的性能,但有時LEFT JOIN會更糟。 LEFT JOIN/IS NULL不能使用反半聯接(將在找到一條記錄後立即停止搜索/掃描),方法與EXISTS相同。使用當前方法,您將選擇符合條件的UVD中的所有記錄,然後對它們進行排序,以便刪除重複項,然後使用這些結果丟棄在子查詢a中找到的記錄。

類似的邏輯適用於INNER JOIN,取而代之的是EXISTS您告訴SQL Server您不在乎UVH中的記錄是什麼,您只是在意有一個記錄。

我確實已做出的另一處改動是改變NOT INNOT EXISTS,這可能是無所作爲,而是NOT IN會導致意外的行爲,如果NULL記錄都存在。

通過這些更改,您應該運行查詢並顯示實際的執行計劃。這將有助於識別瓶頸,並且SQL Server甚至可能會建議加快查詢的索引。

+0

我會用這個解決方案,也很好地解釋了+ 1 –