如何准确检测SQL Server作业是否正在运行并处理已在运行的作业?

Dar*_*inH 9 t-sql sql-server sql-server-agent sql-agent-job

我目前正在使用这样的代码来检测SQL服务器作业是否正在运行.(这是SQL Server 2005,所有SP的)

return (select isnull(  
(select top 1 CASE 
    WHEN current_execution_status = 4 THEN 0
    ELSE 1
    END
from openquery(devtestvm, 'EXEC msdb.dbo.sp_help_job')
where current_execution_status = 4 and
    name = 'WQCheckQueueJob' + cast(@Index as varchar(10))
), 1)
)
Run Code Online (Sandbox Code Playgroud)

那里没有问题,一般来说,它运作得很好.

但....(总是一个但是)

有时,我会调用它,取回"工作未运行"的结果,此时我会尝试通过以下方式启动工作

exec msdb.dbo.sp_start_job @JobName
Run Code Online (Sandbox Code Playgroud)

并且SQL将返回"SQLAgent拒绝启动该作业,因为它已经有待处理的请求".

好.也不是问题.可以想象,在此代码启动之前,可以启动目标作业的一个小窗口,但在检查它是否已启动之后.但是,我可以将它包装在try catch中,然后忽略错误,对吧?

begin try
if dbo.WQIsQueueJobActive(@index) = 0 begin
    exec msdb.dbo.sp_start_job @JobName
    break
end         
end try begin catch
    -- nothing here
end catch
Run Code Online (Sandbox Code Playgroud)

不过这是问题所在.

10次​​中有9次,这很好用.SQL代理将引发错误,它被捕获,并且处理继续,因为作业已经运行,没有任何伤害没有犯规.

但偶尔,我会在"作业历史记录"视图中收到一条消息(请记住上面的代码,以检测特定作业是否正在运行,如果没有实际从另一个作业运行则启动它)说该作业失败,因为"SQLAgent已经拒绝开始工作,因为它已经有待处理的请求".

当然,这是TRY CATCH应该处理的确切错误!

当发生这种情况时,正在执行的工作就会消失,但不能立即从我所知道的情况开始,只是非常接近.我把日志记录到了所有地方并且没有一致性.有一次它失败了,它将在a处,a下一次在b处.在某些情况下,地方A和地方B只有一个

select @var = 'message'
Run Code Online (Sandbox Code Playgroud)

在他们之间.很奇怪.基本上,这项工作似乎是毫不客气地被抛弃的,而且在工作中要执行的任何事情都不会被执行.

但是,如果我删除"exec StartJob"(或者它只调用一次,当我知道目标作业还不能运行时),一切都运行良好,我在工作中的所有处理都会运行.

所有这一切背后的目的是让一个工作由于触发器(以及其他事情)而启动,并且,如果工作已经开始,则实际上没有必要"再次启动它".

有没有人遇到过SQL Agent的Job处理这样的行为?

编辑:当前的控制流程如下:

  1. 更改为表格(更新或插入)...
  2. 触发哪个叫......
  3. 一个调用...的存储过程
  4. sp_Start_Job哪个......
  5. 开始一项特定的工作......
  6. 调用另一个存储过程(称为CheckQueue)...
  7. 执行一些处理和......
  8. 检查几个表并根据其内容可能......
  9. 在另一个作业上调用sp_start_job以启动第二个同时作业来处理其他工作(第二个作业也调用CheckQueue sproc但两个调用对完全独立的数据集进行操作)

Cod*_*ian 4

首先,您有机会了解服务经纪人吗?从你的描述来看,这似乎就是你真正想要的。

区别在于,您不是启动作业,而是将数据放入 SB 队列,SB 会异步调用您的处理过程,并完全回避已运行作业等的问题。它将自动生成/终止其他线程并根据需求指示,它负责顺序等。

这是一个很好的(并且模糊相关的)教程。http://www.sqlteam.com/article/centralized-asynchronous-auditing-with-service-broker

让我们假设您出于某种原因不能使用 SB(但说真的,可以!)。

使用作业 spid 的 context_info 怎么样?

  1. 您的工作调用一个包装程序来单独执行每个步骤。
  2. 包装器过程中的第一条语句是

    DECLARE @context_info VARBINARY(30)
    SET @context_info = CAST('MyJob1' AS VARBINARY)
    SET CONTEXT_INFO @context_info
    
    Run Code Online (Sandbox Code Playgroud)
  3. 当你的过程完成时(或在你的 catch 块中)

    SET CONTEXT_INFO 0x0
    
    Run Code Online (Sandbox Code Playgroud)
  4. 当您考虑给您的工作打电话时,请执行以下操作:

    IF NOT EXISTS (SELECT * FROM master..sysprocesses WITH (NOLOCK) WHERE context_info=CAST('MyJob1' AS VARBINARY))
        EXEC StartJob
    
    Run Code Online (Sandbox Code Playgroud)

当你的包装程序终止或连接关闭时,你的 context_info 就会消失。

您还可以使用全局临时表(即 ##JobStatus),当引用它的所有 spid 断开连接或显式删除它时,它们将消失。

只是一些想法。