2017-08-24 21 views
2

我正在通過CTE更新自加入的結果表。當選擇查看更新的預期結果時,它可以在16秒內返回結果。來自CTE的更新

但是,當我使用相同的語法來嘗試實際更新基礎表時,它非常慢。

對於我的生活,我無法弄清楚爲什麼如此戲劇性的放緩。我經常通過CTE更新表格,與同等選擇相比,它通常更加快速和合理。

我已經在底層表上嘗試過和沒有PrimaryKey/Clustered索引,它沒有區別。

該聯接位於計算列上,因此無法建立索引。

如果SELECT和UPDATE之間的時間差異是兩倍,那麼它就不是問題。這裏的問題是從選擇到更新的時間增加的巨大程度。對於選擇結果爲1323行的16秒,更新其中2行需要59秒,更新4需要1分19秒,更新6需要1分39秒(因此看起來每個附加行增加10秒)。

任何人都可以爲我闡明這一點,並提出一種方法來加速?

這裏的示例代碼:

;WITH CTE AS (SELECT 
       DENSE_RANK() OVER (Order by 
           col1, 
           col2, 
           col3) SetID,     
       COUNT(*) OVER (partition by 
           col1, 
           col2, 
           col3) DupsInSet, 
       row_number() OVER (PARTITION BY 
           col1, 
           col2, 
           col3 
           ORDER BY          
           col4 desc) RowInSet, 
       COUNT(col4) OVER (partition by 
           col1, 
           col2, 
           col3) NonNull, 
       * 
      FROM mytable) 

在16秒後--The下完成,並返回1323行

select b.col4,a.* 

    from cte a 
    join cte b on b.SetID=a.SetID 

    where a.DupsInSet>1 
    and a.NonNull>0 
    and b.RowInSet=1 
    and a.RowInSet>1 
    and b.col4 is not null 
    and a.col4 is null 

從這個--Updating用於運行這麼長時間,我甚至沒有讓它完成 - 作爲一項測試,我將更新限制在前2名。然後這花了59秒來更新2行

UPDATE TOP(2) a 

    SET a.col4=b.col4 

    from cte a 
    join cte b on b.SetID=a.SetID 

    where a.DupsInSet>1 
    and a.NonNull>0 
    and b.RowInSet=1 
    and a.RowInSet>1 
    and b.col4 is not null 
    and a.col4 is null 

爲UPDATE https://www.brentozar.com/pastetheplan/?id=Ske_In3_Z

更新選擇https://www.brentozar.com/pastetheplan/?id=ByItSnhuW

實際執行計劃實際執行計劃:

以從SqlZim它跑了4分鐘的建議沒有完成,在這一點上,我停止了。

然而,隨着

  1. 在模式的改變(謝謝SqlZim)從幾個VARCHAR(MAX)列到VARCHAR(?)在哪裏?是MAX(LEN(列))

  • 一些修改SqlZims建議的查詢,則更新能夠在23秒運行!...其大約是3,300倍:)
  • 這裏是最後一個查詢(除非有人可以使其工作,而不必列出所有darn列,例如加入對SETID):

    (注意,熱膨脹係數仍用於從600K +行下過濾原始表)

    ;WITH CTE AS (SELECT        
           COUNT(*) OVER (partition by 
               [col1] 
               ,[col2] 
               ,[col3] 
               ,[col4] 
               ,[col5] 
               ,[col6] 
               ,[col7] 
               ,[col8] 
               ,[col9] 
               ,[col10] 
               ,[col11] 
               ,[col12] 
               ,[col13] 
               ,[col14] 
               ,[col15] 
               ,[col16] 
               ,[col17] 
               ,[col18] 
               ,[col19] 
               ,[col20] 
               ,[col21] 
               ,[col22] 
               ,[col23]) DupsInSet, 
           COUNT(col24) OVER (partition by 
               [col1] 
               ,[col2] 
               ,[col3] 
               ,[col4] 
               ,[col5] 
               ,[col6] 
               ,[col7] 
               ,[col8] 
               ,[col9] 
               ,[col10] 
               ,[col11] 
               ,[col12] 
               ,[col13] 
               ,[col14] 
               ,[col15] 
               ,[col16] 
               ,[col17] 
               ,[col18] 
               ,[col19] 
               ,[col20] 
               ,[col21] 
               ,[col22] 
               ,[col23]) NonNull, 
           * 
          FROM mytable 
        ) 
    
    update a 
        set a.col24 = b.col24 
    from cte a 
        cross apply (
        select top 1 i.col24 
        from cte i 
        where (i.col1=a.col1 OR (i.col1 is null AND a.col1 is null)) 
         and (i.col2=a.col2 OR (i.col2 is null AND a.col2 is null)) 
         and (i.col3=a.col3 OR (i.col3 is null AND a.col3 is null)) 
         and (i.col4=a.col4 OR (i.col4 is null AND a.col4 is null)) 
         and (i.col5=a.col5 OR (i.col5 is null AND a.col5 is null)) 
         and (i.col6=a.col6 OR (i.col6 is null AND a.col6 is null)) 
         and (i.col7=a.col7 OR (i.col7 is null AND a.col7 is null)) 
         and (i.col8=a.col8 OR (i.col8 is null AND a.col8 is null)) 
         and (i.col9=a.col9 OR (i.col9 is null AND a.col9 is null)) 
         and (i.col10=a.col10 OR (i.col10 is null AND a.col10 is null)) 
         and (i.col11=a.col11 OR (i.col11 is null AND a.col11 is null)) 
         and (i.col12=a.col12 OR (i.col12 is null AND a.col12 is null)) 
         and (i.col13=a.col13 OR (i.col13 is null AND a.col13 is null)) 
         and (i.col14=a.col14 OR (i.col14 is null AND a.col14 is null)) 
         and (i.col15=a.col15 OR (i.col15 is null AND a.col15 is null)) 
         and (i.col16=a.col16 OR (i.col16 is null AND a.col16 is null)) 
         and (i.col17=a.col17 OR (i.col17 is null AND a.col17 is null)) 
         and (i.col18=a.col18 OR (i.col18 is null AND a.col18 is null)) 
         and (i.col19=a.col19 OR (i.col19 is null AND a.col19 is null)) 
         and (i.col20=a.col20 OR (i.col20 is null AND a.col20 is null)) 
         and (i.col21=a.col21 OR (i.col21 is null AND a.col21 is null)) 
         and (i.col22=a.col22 OR (i.col22 is null AND a.col22 is null)) 
         and (i.col23=a.col23 OR (i.col23 is null AND a.col23 is null)) 
         and i.col24 is not null 
        order by col24 desc 
    ) b 
    where a.col24 is null 
    and a.DupsInSet>1 
    and a.NonNull>0 
    
    +4

    使用[粘貼該計劃@ brentozar.com分享您的執行計劃](https://www.brentozar.com/pastetheplan/)這裏是說明:[如何使用粘貼計劃](https://www.brentozar.com/pastetheplan/instructions/)。樣本數據和期望的結果也會有所幫助。 – SqlZim

    +0

    @SqlZim - 已發佈編輯以添加執行計劃的鏈接。鏈接到選擇的示例行https://ibb.co/huyQhQ – tentrade

    +1

    爲什麼要從CTE更新CTE?這是什麼意思,因爲它僅在CTE創建後的第一個聲明中可用? – HLGEM

    回答

    0

    從我可以告訴你要更新col4它在哪裏nullcol1, col2, col3上匹配的行開始,並且您希望使用基於col4 desc的第一個非nullcol4

    你能做到這一點,像這樣:

    update a 
        set a.col4 = b.col4 
    from mytable a 
        cross apply (
        select top 1 i.col4 
        from mytable i 
        where i.col1 = a.col1 
         and i.col2 = a.col2 
         and i.col3 = a.col3 
         and i.col4 is not null 
        order by col4 desc 
    ) b 
    where a.col4 is null 
    

    你也可以支持此操作有支撐指數,如:

    create nonclustered index ix_mytable_col1_col2_col3_inc_col4 
        on dbo.mytable (col1,col2,col3) 
        include (col4); 
    
    +0

    謝謝。我認爲這將有助於完成更新。然而,我希望找到一種加速CTE版本的方法,因爲我正在處理的真實工作需要在整個數據中進行諸如此類的操作,並且使用CTE可以更容易地引用它。我會在那裏放一會兒,看看人們想出了什麼。如果一天左右沒有更好的事情發生,那麼我會將你的回答標記爲答案。 – tentrade

    +0

    爲什麼不使用#temp表格而不是CTE?您可以索引#temp表並通過多個步驟引用它。 – HLGEM

    +0

    @HLGEM - 因爲用於計算4個計算列的列在整個過程中發生變化(總共涉及25列)。因此,我需要每次創建一個新的臨時表或更新4個計算列。通過CTE,我可以通過在必要的欄目中註釋/進行簡單的快速編輯。但是,就目前而言,由於使用此方法需要花費更多時間進行更新,臨時表可能是一個有價值的解決方案。 – tentrade