如何删除SQL 2005中的记录以保持事务日志的检查

时间:2009-09-09 19:30:53

标签: sql-server tsql

我正在运行以下存储过程来删除大量记录。我知道DELETE语句写入事务日志并删除许多行将使日志增长。

我已经研究了创建表和插入记录以保留然后截断源的其他选项,这种方法对我来说不起作用。

如何确保我不会不必要地增加事务日志,从而使我的存储过程更加高效?

CREATE PROCEDURE [dbo].[ClearLog] 
(
  @Age int = 30
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

  -- DELETE ERRORLOG
  WHILE EXISTS ( SELECT [LogId]  FROM [dbo].[Error_Log] WHERE DATEDIFF( dd, [TimeStamp], GETDATE() ) > @Age )
   BEGIN
    SET ROWCOUNT 10000
    DELETE [dbo].[Error_Log] WHERE DATEDIFF( dd, [TimeStamp], GETDATE() ) > @Age

    WAITFOR DELAY '00:00:01'
    SET ROWCOUNT 0
   END
END

5 个答案:

答案 0 :(得分:4)

我将如何做到这一点:

CREATE PROCEDURE [dbo].[ClearLog] (  
@Age int = 30)
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @d DATETIME
        , @batch INT;
    SET @batch = 10000;
    SET @d = DATEADD( dd, -@Age, GETDATE() )
    WHILE (1=1)
    BEGIN
        DELETE TOP (@batch) [dbo].[Error_Log]  
        WHERE [Timestamp] < @d;
        IF (0 = @@ROWCOUNT)
            BREAK
    END
END
  • 使Tiemstamp比较SARGable
  • 在批处理开始时分隔GETDATE()以产生一致的运行(否则它可以在无限循环中阻塞,因为新记录的'age'与旧的记录一样)。
  • 使用TOP代替SET ROWCOUNT(deprecatedUsing SET ROWCOUNT will not affect DELETE, INSERT, and UPDATE statements in the next release of SQL Server.
  • 检查@@ ROWCOUNT以打破循环而不是冗余SELECT

答案 1 :(得分:1)

假设您可以选择在分区方案上重建错误日志表,则可以选择在日期对表进行分区并交换分区。谷歌搜索'alter table switch partition'进一步挖掘。

答案 2 :(得分:1)

如何更经常地运行它,每次删除更少的行?每30分钟运行一次:

CREATE PROCEDURE [dbo].[ClearLog] 
(
  @Age int = 30
)
AS
BEGIN
    SET NOCOUNT ON;
    SET ROWCOUNT 10000 --I assume you are on an old version of SQL Server and can't use TOP
    DELETE dbo.Error_Log Where Timestamp>GETDATE()-@Age
    WAITFOR DELAY '00:00:01' --why???
    SET ROWCOUNT 0
END

它处理日期的方式不会截断时间,每次只删除30分钟的数据。

答案 3 :(得分:1)

如果您的数据库处于完全恢复模式,那么最小化删除语句影响的唯一方法是“将它们分开” - 只在“事务间隔”期间删除这么多。例如,如果每小时执行t-log备份,则每小时仅删除20,000行。这可能不会一次性降低所有需求,但是24小时后或一周后会出现问题吗?

如果您的数据库处于SIMPLE或BULK_LOGGED模式,则应将删除分解为块。但是,既然你已经这样做了,我不得不猜测你的数据库处于完全恢复模式。 (那,或者调用过程的连接可能是事务的一部分。)

答案 4 :(得分:0)

我过去使用过的解决方案是暂时将恢复模式设置为“Bulk Logged”,然后在存储过程结束时返回“Full”:

DECLARE @dbName NVARCHAR(128);
SELECT @dbName = DB_NAME();

EXEC('ALTER DATABASE ' + @dbName + ' SET RECOVERY BULK_LOGGED')

WHILE EXISTS (...)
BEGIN
    -- Delete a batch of rows, then WAITFOR here
END

EXEC('ALTER DATABASE ' + @dbName + ' SET RECOVERY FULL')

这将显着减少大批量的事务日志消耗。 我不喜欢它为整个数据库设置恢复模型(不仅仅是这个会话),但它是我能找到的最佳解决方案。