2017-05-25 77 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で)何も返さないテーブルためのTRUNCATEステートメントのLCK_M_SCH_Sロックがあるため。私もすることはできません:

SELECT object_id('SC.A_TABLE') 

他の興味深いものがあります。私はときどきSSMSを介して手順の実行を停止し、その後も私はCOUNTを取ることができませんまたはobject_idを選択します。実行はでsys.sysprocessesと思われ、ロックを解除するためにクエリウィンドウを閉じる必要があります。私はトランザクションを使用し、実行を中止して中間状態にしておくので、それは疑わしいですが、わかりません。

テーブルを切り詰めるには、テーブルに外部キーやインデックスがないため、時間がかかりません。

何が問題なのですか?私はこれの代わりにDELETEを使うかもしれませんが、私はTRUNCATEがはるかに速いことを知っています。

EDIT:問題なくDELETE代わりのTRUNCATE作品ところで、私は最後の手段としてのみ、それを使用したいです。

+0

奇妙なことに、スピード(ログに記録されていない)のためにトランケートを使用していますが、トランザクションに入れています...ログに記録されませんか?あなたは日常的にテーブルを削除しており、簡単にそれを置き換えることができるので、ここでトランザクションが必要ですか?代わりにSSISを使用できますか。その一括読み込みはINSERT INTO(2回)より高速です。 – JiggsJedi

+0

興味深いことに、トランザクションのトランケートは、削除の代わりにページの割り当て解除を記録するようです... https://stackoverflow.com/questions/1522931/truncate-table-within-transaction – JiggsJedi

+0

@JiggsJediまさにこの手順は、別のプロシージャによって呼び出され、別のプロシージャから呼び出され、トランザクションの終わりから終わりまでの間に終了します。 – sotn

答えて

1

切り捨てがあなたのバッグではなく、あなたがクラッシュ停止にTLOGを持参せずに実行するには、削除のためにあまりにも多くの行を持っている場合は、必ずオプションUBW(醜いのために、しかし、実行可能)があります:作成しますテーブルのクローン、それにローをロードしてから(そしてトランザクション内で)、すべてを切り替えます。

オプションUbW2は、この概念をベースにしています。常に2つのテーブルが作成されています.1つは空、1つは完全です。空のテーブルにロードして、そのテーブルを指すようにシノニムのビューを変更します。

オプションLUbW(less than ...)パーティションを使用する:スイッチテーブルにデータをロードし、それをパーティション機能としていくつかのフラグを使用してパーティションとして移動します。

これらのすべてには、より多くの作業とコードが必要です。似たような状況があり、当社のデータウェアハウス用にオプションUbW2を使用すると、1時間ごとにダウンタイムなしで何百万もの行を「アクティブな」テーブルにロードしたり、消費者が不一致なデータを見る危険がありません。

+0

UbW2は実際に私が推測するこの種の状況に入るかなり良い方法です。この場合、 'TRUNCATE'の代わりに' DELETE'に変更することはできますが、これよりも状況が複雑な場合は、UbW2を使用します。そして、私は彼らが醜いとは思わない:)(その略語btwのおかげで、良いもの)私はかなりLUbWを理解していない。なぜこれはあまりおかしなことだと思いますか?それはUbW2に似ています。相違点は、私がビューを変更し、もう1つは、この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は、トランザクションがコミット可能であることを確認します(すべてではありません)。トランザクションが存在し、コミット可能である場合、Xact_State()は1を返すだけなので、@ @ TranCountは誠実であり、おそらく冗長です。しかし、はい、常にチェックするのが賢明です。それはGoogleの詳細情報です。 –

関連する問題