1

に基づいて巨大なテーブルからレコードを削除するためにストアド プロシージャを使用するデータベース パージ プロセスがありますExpire Date。これは 3 週間ごとに実行され、約 300 万のレコードを削除します。

現在、多くの問題を引き起こしているデータのパージに約 5 時間かかっています。コードを効率的に書く方法がたくさんあることは知っていますが、アイデアがありません。正しい方向に助けてください。

--Stored Procedure
CREATE PROCEDURE [dbo].[pa_Expire_StoredValue_By_Date]
     @ExpireDate DateTime, @NumExpired int OUTPUT, @RunAgain int OUTPUT 
AS
   -- This procedure expires all the StoredValue records that have an ExpireDate less than or equal to the DeleteDate provided
   -- and have QtyUsed<QtyEarned
   -- invoked by DBPurgeAgent

   declare @NumRows int  

   set nocount on  
   BEGIN TRY    
     BEGIN TRAN T1  

        set @RunAgain = 1;  
        select @NumRows = count(*) from StoredValue where ExpireACK = 1;  

        if @NumRows = 0 
        begin  
           set rowcount 1800;  -- only delete 1800 records at a time  

           update StoredValue with (RowLock) 
           set ExpireACK = 1 
           where ExpireACK = 0 
             and ExpireDate < @ExpireDate 
             and QtyEarned > QtyUsed;  

           set @NumExpired=@@RowCount;  
           set rowcount 0  
        end  
        else begin  
           set @NumExpired = @NumRows;  
        end  

        if @NumExpired = 0 
        begin  -- stop processing when there are no rows left  
           set @RunAgain = 0;  
        end  
        else begin  
            Insert into SVHistory (LocalID, ServerSerial, SVProgramID, CustomerPK, QtyUsed, Value, ExternalID, StatusFlag, LastUpdate, LastLocationID, ExpireDate, TotalValueEarned, RedeemedValue, BreakageValue, PresentedCustomerID, PresentedCardTypeID, ResolvedCustomerID, HHID)  
                select 
                   SV.LocalID, SV.ServerSerial, SV.SVProgramID, SV.CustomerPK, 
                   (SV.QtyEarned-SV.QtyUsed) as QtyUsed, SV.Value, SV.ExternalID, 
                   3 as StatusFlag, getdate() as LastUpdate, 
                   -9 as LocationID, SV.ExpireDate, SV.TotalValueEarned, 
                   0 as RedeemedValue, 
                   ((SV.QtyEarned-SV.QtyUsed)*SV.Value*isnull(SVUOM.UnitOfMeasureLimit, 1)), 
                   PresentedCustomerID, PresentedCardTypeID, 
                   ResolvedCustomerID, HHID   
                from 
                   StoredValue as SV with (NoLock) 
                Left Join 
                   SVUnitOfMeasureLimits as SVUOM on SV.SVProgramID = SVUOM.SVProgramID  
                where 
                   SV.ExpireACK = 1  

           Delete from StoredValue with (RowLock) where ExpireACK = 1;  
        end  

        COMMIT TRAN T1;  
    END TRY  
    BEGIN CATCH  
       set @RunAgain = 0;  

       IF @@TRANCOUNT > 0 BEGIN  
          ROLLBACK TRAN T1;  
       END  

       DECLARE @ErrorMessage NVARCHAR(4000);  
       DECLARE @ErrorSeverity INT;  
       DECLARE @ErrorState INT;  

       SELECT 
           @ErrorMessage = ERROR_MESSAGE(), 
           @ErrorSeverity = ERROR_SEVERITY(), 
           @ErrorState = ERROR_STATE();  

       RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);  
    END CATCH
4

2 に答える 2

0

最初に、実行 opplan を見て、何が遅くなっているのかを調べます。それはinsert文ですか、それともdeleteですか?

挿入での計算のために、それが遅い部分であると思われます(テーブルにトリガーまたはカスケード削除がない限り)。履歴テーブルを変更して、計算で使用する列を持ち、計算フィールドで NULL を許可することができます。これで、そのテーブルにすばやく挿入してから削除できます。ジョブの別のステップで、計算を更新できます。これにより、少なくとも短い期間は拘束されますが、履歴テーブルへのアクセス方法によっては、可能である場合と不可能である場合があります。

もう 1 つの簡単な方法は、テーブルの名前を StoredValue から StoredValueRaw に変更し、アクティブなレコードのみを表示する StoredValue というビューを作成することです。その後、レコードを削除するジョブは 15 分程度ごとに実行され、一度に数件のレコードのみが削除されます。これにより、実際の削除に時間がかかる場合でも、ユーザーへの影響がはるかに少なくなる可能性があります。有効期限が切れたと識別された時点で、レコードを履歴テーブルに配置する必要がある場合があります。

このプロセスも 3 週間ごとに実行することを再考する必要があります。処理する必要があるレコードが少ないほど、処理が速くなります。

于 2013-07-15T14:29:07.513 に答える