2017-05-25 73 views
0

我有一個存儲過程截斷一個不是很大的表(2M記錄,但將來會變大),然後重新填充它。示例版本如下所示:截斷表不釋放LCK_M_SCH_S

ALTER PROCEDURE [SC].[A_SP] 
AS 
BEGIN 

BEGIN TRANSACTION; 

BEGIN TRY 
    TRUNCATE TABLE SC.A_TABLE 

    IF OBJECT_ID('tempdb..#Trans') IS NOT NULL DROP TABLE #Trans 

    SELECT 
     * 
    INTO 
     #Trans 
    FROM 
    (
     SELECT 
      ... 
     FROM 
      B_TABLE trans (NOLOCK) 
     INNER JOIN 
      ... (NOLOCK) ON ... 
     LEFT OUTER JOIN 
      ... (NOLOCK) ON ... 
     ... 
    ) AS x 

    INSERT INTO 
     SC.A_TABLE 
     (
      ... 
     ) 
    SELECT 
     ... 
    FROM 
     #Trans (NOLOCK) 

    DROP TABLE #Trans 
END TRY 
BEGIN CATCH 
    IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION; 
    THROW 
END CATCH 

IF @@TRANCOUNT > 0 
    COMMIT TRANSACTION; 
END 

此過程需要幾個小時才能工作。有時候我想借此COUNT看到用多少完成:

SELECT COUNT(*) FROM A_TABLE (NOLOCK) 

這並不返回任何東西(甚至NOLOCK),因爲LCK_M_SCH_S鎖在桌子上,因爲TRUNCATE聲明。我什至不能這樣做:

SELECT object_id('SC.A_TABLE') 

另一個有趣的事情是;我有時會通過SSMS停止執行該程序,甚至在此之後我不能採取COUNT或選擇object_id。執行似乎suspendedsys.sysprocesses,我必須關閉查詢窗口,使其釋放鎖。我懷疑這是因爲我使用交易,並通過停止執行將其置於中間狀態,但我不確定。

我知道截斷表不會花費太多時間,因爲表沒有任何外鍵或索引。

可能是什麼問題?我可以用DELETE代替這個,但我知道TRUNCATE在這裏會快很多。

編輯:的DELETE代替TRUNCATE工作沒有任何問題,順便說一句,但我只是想用它作爲最後的手段。

+0

好奇的是,你正在使用截斷速度(因爲它沒有記錄),但你把它放在一個事務中......是不是讓它記錄下來?你是否需要在這裏進行交易,因爲你經常消除桌面並可以輕鬆替換它?你可以使用SSIS,它的批量加載比INSERT INTO(兩次)要快。 – JiggsJedi

+0

有趣的是,它似乎在一個事務日誌中截斷一個頁面取消分配而不是刪除... https://stackoverflow.com/questions/1522931/truncate-table-within-transaction – JiggsJedi

+0

@JiggsJedi確切地說,這個過程被稱爲通過另一個程序,而另一個程序則從另一個程序中調用,這是事務性的端對端。 – sotn

回答

1

如果截斷是不是你的包,你有太多的行以刪除而不帶來的TLog到崩潰停止執行,總是有選擇UBW(醜陋,但可行的):創建一個表的克隆,將行加載到那個表中,然後(並在一個事務中),切換一切。

選項UbW2建立在這個概念 - 有兩個表總是建立 - 一個空的,一個完整的。加載到空表中,然後修改同義詞視圖以指向該表。

選項LUbW(不那麼醜陋...)涉及使用分區:將數據加載到切換表中,然後使用某個標誌作爲分區函數將其作爲分區移動。

所有這些都需要更多的工作和代碼。我們有類似的情況,並使用選項UbW2作爲我們的數據倉庫,允許我們將數百萬行加載到「活動」表中,而不是每小時停機一次,也不會讓消費者看到不一致的數據。

+0

UbW2實際上是一種很不錯的方式,我想這種情況下。在這種情況下,我可以更改爲'DELETE'而不是'TRUNCATE',但如果情況比這更復雜,我會使用UbW2。我不認爲他們很醜:)(感謝那個縮寫btw,很好)我雖然不太瞭解LUBW。你爲什麼認爲這不那麼醜陋?這似乎與UbW2類似。不同之處在於其中一個我會改變視圖,而另一個我需要在分區函數中翻轉這個標誌,我每次運行這個SP的權利? – sotn

+0

減少醜陋,因爲您不必擔心指向活動表的「指針」視圖或同義詞,因爲您在交易中切入和切出,因此表是源。否則,視圖或同義詞將成爲消費者閱讀的來源。此外,任何同義詞或視圖更改都必須等到所有消費者都不在時。或者倉庫有大約200張表,大約需要2分鐘左右切換所有的同義詞。如果我再做一次,我可能會自己去做最後一個選擇,但我們的解決方案非常可行且穩定。 –

0

最好你會得到的是轉移一些繁重的交易。也就是說,Sql Server按設計工作100%。

ALTER PROCEDURE [SC].[A_SP] 
AS 
BEGIN 

IF OBJECT_ID('tempdb..#Trans') IS NOT NULL DROP TABLE #Trans 

    SELECT 
     * 
    INTO 
     #Trans 
    FROM 
    (
     SELECT 
      ... 
     FROM 
      B_TABLE trans (NOLOCK) 
     INNER JOIN 
      ... (NOLOCK) ON ... 
     LEFT OUTER JOIN 
      ... (NOLOCK) ON ... 
     ... 
    ) AS x 

BEGIN TRANSACTION; 

BEGIN TRY 
    TRUNCATE TABLE SC.A_TABLE 

    INSERT INTO 
     SC.A_TABLE 
     (
      ... 
     ) 
    SELECT 
     ... 
    FROM 
     #Trans (NOLOCK) 

    DROP TABLE #Trans 

    If @@TranCount >0 And Xact_State() = 1 
     Commit Transaction; 
END TRY 
BEGIN CATCH 
    IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION; 
    THROW 
END CATCH 
+0

但是這個仍然會把這個模式鎖定在表格上嗎?我猜只要我使用'TRUNCATE',我會面對這個問題。我猜,在這種情況下最好使用DELETE。因爲'DELETE'放置了行鎖而不是模式鎖,並且使用'NOLOCK'子句的查詢可以毫無問題地工作。 – sotn

+0

我注意到您添加了'Xact_State()= 1'控件。您是否添加了它,因爲這是一種很好的做法,或者在此查詢中是否真的需要它?因爲我在開始時明確地啓動了事務並且查詢在'try..catch'中。如果發生錯誤,它會轉到catch子句,而不是嘗試提交事務。有沒有'Xact_State'保存我的屁股這個sp的情況? – sotn

+0

Xact_State()= 1將確保事務是可提交的(不是全部都是)。 @@ TranCount實際上可能是多餘的,因爲如果存在一個事務並且它可以提交,Xact_State()將只返回1。但是,是的,檢查總是明智的。谷歌它的更多信息。 –