【问题标题】:Using an SQL Agent Job to call procedures in a loop使用 SQL 代理作业循环调用过程
【发布时间】:2009-10-21 14:11:42
【问题描述】:

我正在 SQL Enterprise Manager 2000 上完成一项工作,以复制和删除几个数据库表中的记录。我们已经运行了一个直接的批量复制和删除存储过程,但它可能会在数百万行上运行它,因此会挂起服务器。我有兴趣尝试一次以 100 个记录块运行该服务,因此服务器不会停止运行(这是一个实时 Web 数据库)。我希望这项服务每晚运行一次,这就是我将它放在代理工作中的原因。有没有办法循环调用实际执行复制和删除的存储过程,然后在每次调用之间“休眠”以让服务器有时间赶上?我知道有 WAITFOR 命令,但我不确定这是否会保留处理器或让它同时运行其他查询。

谢谢!

【问题讨论】:

    标签: stored-procedures sql-server-agent


    【解决方案1】:

    “分块化”您的删除是删除大量数据而不会使事务日志文件膨胀的首选方法。 BradC 的帖子就是一个合理的例子。

    管理此类循环最好在单个存储过程中完成。为了随着时间的推移分散这些工作,我仍然会将其保留在程序中。如果您认为有必要处理可能的并发问题,则在循环中插入 WAITFOR 将在每组删除之间放置一个“暂停”。使用 SQL 代理作业来确定过程何时开始 - 如果您需要确保它在某个时间停止,也可以将其放入循环中。

    我对这段代码的看法是:

    --  NOTE: This is a code sample, I have not tested it
    CREATE PROCEDURE ArchiveData
    
        @StopBy DateTime
        --  Pass in a cutoff time.  If it runs this long, the procedure will stop.
    AS
    
    DECLARE @LastBatch  int
    
    SET @LastBatch = 1
    --  Initialized to make sure the loop runs at least once
    
    
    WHILE @LastBatch > 0
     BEGIN
    
        WAITFOR DELAY '00:00:02'
        --  Set this to your desired delay factor
    
        DELETE top 1000  --  Or however many per pass are desired
         from SourceTable
        --  Be sure to add a where clause if you don't want to delete everything!
    
        SET @LastBatch = @@rowcount
    
        IF getdate() > @StopBy
            SET @LastBatch = 0
    
     END
    
    RETURN 0
    

    嗯。重读您的帖子意味着您想在删除之前先将数据复制到某个地方。为此,我设置了一个临时表,并在循环内首先截断临时表,然后复制前 N 个项目的主键,通过连接到临时表插入“存档”表,然后也通过连接到临时表来删除源表。 (只是比直接删除复杂一点,不是吗?)

    【讨论】:

    • 你是对的。在其中两个表中,我在技术上“归档”数据,例如将其复制到同一服务器上的另一个数据库,然后删除现有记录。在另一个表中,我只是删除在某个日期之前创建的所有记录。我考虑过使用临时表来协助转移,我最终可能会这样做。我会试试你的建议,看看结果如何。谢谢!
    【解决方案2】:

    不用担心循环之间的等待,SQL Server 应该处理您的维护工作和服务器上的常规活动之间的争用。

    在这些类型的情况下,真正导致问题的原因是整个删除过程同时发生在一个事务中。这会炸毁数据库的日志,并可能导致您所遇到的各种问题。

    使用这样的循环删除可管理的块:

    DECLARE @i INT
    SET @i = 1
    
    SET ROWCOUNT 10000
    
    WHILE @i > 0
    BEGIN
        BEGIN TRAN
            DELETE TOP 1000 FROM dbo.SuperBigTable
            WHERE RowDate < '2009-01-01'
        COMMIT
    
        SELECT @i = @@ROWCOUNT
    END
    SET ROWCOUNT 0
    

    您可以对您的副本使用类似的逻辑。

    【讨论】:

    • 感谢您的反馈。这段代码是在存储过程中执行(并从代理作业中调用)还是仅在代理作业本身中执行,这有关系吗?
    • 不,不是。只是个人喜好/公司政策。
    【解决方案3】:

    WAITFOR 会让其他进程“试一试”。我已经使用这种技术来阻止大型 DELETE 锁定机器。创建一个 WHILE 循环,删除一行行,然后等待几秒钟(或更少,只要合适)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-06
      • 1970-01-01
      • 2013-01-21
      • 1970-01-01
      • 2016-05-12
      • 1970-01-01
      • 2023-03-12
      • 2021-05-02
      相关资源
      最近更新 更多