Sql server 如何在SQL 2005中删除记录保持事务日志处于检查状态

Sql server 如何在SQL 2005中删除记录保持事务日志处于检查状态,sql-server,tsql,Sql Server,Tsql,我正在运行以下存储过程来删除大量记录。我知道DELETE语句会写入事务日志,删除许多行会使日志增长 我已经研究了创建表和插入记录以保留,然后截断源的其他选项,这种方法对我不起作用 如何使下面的存储过程更高效,同时确保事务日志不会不必要地增长? CREATE PROCEDURE [dbo].[ClearLog] ( @Age int = 30 ) AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- int

我正在运行以下存储过程来删除大量记录。我知道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

我会这样做:

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
创建过程[dbo]。[ClearLog](
@年龄(整数=30)
作为
开始
不计数;
声明@d DATETIME
,@batch INT;
设置@batch=10000;
设置@d=DATEADD(dd,-@Age,GETDATE())
而(1=1)
开始
删除TOP(@batch)[dbo]。[错误日志]
其中[时间戳]<@d;
如果(0=@@ROWCOUNT)
打破
结束
结束
  • 进行Tiemstamp比较
  • 在批处理开始时分离GETDATE(),以生成一致的运行(否则,当删除旧记录时,新记录“过时”时,它会在无限循环中阻塞)
  • 使用TOP而不是SET ROWCOUNT(:
    使用SET ROWCOUNT不会影响SQL Server下一版本中的DELETE、INSERT和UPDATE语句。
  • 选中@@ROWCOUNT以中断循环,而不是重复选择

假设您可以选择在分区方案上重建错误日志表,一个选项是按日期对表进行分区并交换分区。在谷歌上搜索“ALTERTABLE switch partition”以进一步挖掘。

您是否可以更频繁地运行它,每次删除更少的行?每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分钟的数据。

我过去使用的解决方案是将恢复模式临时设置为“大容量日志”,然后在存储过程结束时返回到“完全”:

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')
这将大大减少大批量的事务日志消耗。
我不喜欢它为整个数据库设置恢复模型(不仅仅是本次会话),但它是我能找到的最好的解决方案。

如果您的数据库处于完全恢复模式,将delete语句的影响降至最低的唯一方法是“将它们隔开”——在“事务间隔”期间只删除这么多。例如,如果您每小时执行一次t-log备份,则每小时仅删除20000行。这可能不会一下子就把你所需要的全部丢掉,但在24小时后,或者一周后,事情会变得更平衡吗


如果您的数据库处于简单或大容量日志模式,则应将删除分为多个块。但是,既然您已经这样做了,我猜您的数据库处于完全恢复模式。(或者调用过程的连接可能是事务的一部分。)

“我已经研究了创建表和插入记录以保留并截断源的其他选项,这种方法对我不起作用。”为什么?这个问题的答案会影响可能的解决方案。如果我截断原始表,会有某种超时吗?这不会导致应用程序在尝试写入日志表时出现问题吗?这是我关心的问题。数据库恢复模式设置为什么?(完整、批量记录、简单)另一个好技巧——你能在删除发生时留出空间,从而将其影响降至最低吗?@Philip Kelley,这是“每30分钟”的想法,但仍然限制在10000,这将通过其他“真实”用户事务分散负载啊。这会让程序一直在运行,不是吗?我建议将计时移到SQL代理作业上,该作业每30分钟调用一次过程来删除行。我刚刚在900万行上测试了这一点,一次删除1000行。对于任何不熟悉SARGable这个术语的人来说,Timestamp字段应该被索引。此外,这种方法可能仅适用于简单恢复模式,即每次删除后清空事务日志。