从存储过程执行 SQL Server 代理作业并返回作业结果
Posted
技术标签:
【中文标题】从存储过程执行 SQL Server 代理作业并返回作业结果【英文标题】:Executing SQL Server Agent Job from a stored procedure and returning job result 【发布时间】:2012-08-28 06:16:33 【问题描述】:需要有一个存储过程调用 SQL Server 代理作业并返回作业是否成功运行。
目前为止
CREATE PROCEDURE MonthlyData
AS
EXEC msdb.dbo.sp_start_job N'MonthlyData'
WAITFOR DELAY '000:04:00'
EXEC msdb.dbo.sp_help_jobhistory @job_name = 'MonthlyData'
GO
从哪个开始作业,如果作业运行成功与否,最好的返回方式是什么?
Ok 进行了编辑并使用了 WAITFOR DELAY,因为该作业通常在 3-4 分钟之间运行,从不超过 4 分钟。该作业是否有效,但有更有效的方法吗?
【问题讨论】:
好问题。由于“等待代理工作完成”被定向到这个问题,但你的问题实际上并没有提到它,我正在为 SEO 添加这个评论:) 【参考方案1】:您可以运行查询:
EXEC msdb.dbo.sp_help_jobhistory
@job_name = N'MonthlyData'
它将返回一个列 run_status。状态是:
0 - Failed
1 - Succeeded
2 - Retry
3 - Canceled
更多信息MSDN
编辑:您可能想要轮询您的工作并确保它已被执行。您可以从 sp_help_job 过程中获取此信息。当此过程返回4
状态时,表示作业处于空闲状态。
然后就可以安全地检查它的运行状态了。
您可以使用以下代码进行投票:
DECLARE @job_status INT
SELECT @job_status = current_execution_status FROM OPENROWSET('SQLNCLI', 'Server=.;Trusted_Connection=yes;','exec msdb.dbo.sp_help_job @job_name = ''NightlyBackups''')
WHILE @job_status <> 4
BEGIN
WAITFOR DELAY '00:00:03'
SELECT @job_status = current_execution_status FROM OPENROWSET('SQLNCLI', 'Server=.;Trusted_Connection=yes;','exec msdb.dbo.sp_help_job @job_name = ''NightlyBackups''')
END
EXEC msdb.dbo.sp_help_jobhistory
@job_name = N'NightlyBackups' ;
GO
此代码将检查状态,等待 3 秒,然后重试。一旦我们获得 4 的状态,我们就知道工作已完成,并且可以安全地检查工作历史记录。
【讨论】:
您可能想表明您基本上需要轮询,直到相关工作达到“最终状态”。 所以一旦我开始工作,我就需要轮询直到工作完成,然后执行 jobhistory? @ChristianK 你能告诉我们更多关于 Job Agent 投票的信息吗? 这段代码有点问题。在工作实际开始并修改其@job_status
之前,sp_start_job
之后可能会有延迟。有时当我使用此方法时,循环会立即终止,因为 @job_status
与上次运行时相比仍为 4。一种解决方法是在检查初始作业状态之前放置WAITFOR DELAY '00:00:04'
,以使 SQL 代理有机会启动作业。不过感觉很hackish。还有其他想法吗?
这个人遇到了同样的问题,并得到了与我相同的(黑客)解决方案。 interworks.com/blogs/bbickell/2010/01/15/…【参考方案2】:
对于所有不允许使用 OPENROWSET 命令的人,这可能会有所帮助。我在这里找到了解决方案的起点:
http://social.msdn.microsoft.com/Forums/en-US/89659729-fea8-4df0-8057-79e0a437b658/dynamically-checking-job-status-with-tsql
这取决于 msdb.dbo.sysjobactivity 表的某些列在作业以一种或另一种方式完成后首先填充的事实。
-- Start job
DECLARE @job_name NVARCHAR(MAX) = 'JobName'
EXEC msdb.dbo.sp_start_job @job_name = @job_name
-- Wait for job to finish
DECLARE @job_history_id AS INT = NULL
WHILE @time_constraint = @ok
BEGIN
SELECT TOP 1 @job_history_id = activity.job_history_id
FROM msdb.dbo.sysjobs jobs
INNER JOIN msdb.dbo.sysjobactivity activity ON activity.job_id = jobs.job_id
WHERE jobs.name = @job_name
ORDER BY activity.start_execution_date DESC
IF @job_history_id IS NULL
BEGIN
WAITFOR DELAY '00:00:10'
CONTINUE
END
ELSE
BREAK
END
-- Check exit code
SELECT history.run_status
FROM msdb.dbo.sysjobhistory history
WHERE history.instance_id = @job_history_id
您可能需要检查允许 WHILE 循环运行多长时间。我选择将该部分排除在示例之外。
Microsoft 退出代码等指南: http://technet.microsoft.com/en-us/library/ms174997.aspx
【讨论】:
我喜欢你的代码,但我不喜欢超时。我已经发布了我自己的没有超时的解决方案。代理作业步骤可以配置超时,我认为这是我希望拥有它们的地方。【参考方案3】:我可能有点晚了,但我发现以下查询对我有用。它将给出执行时间和执行结束时间。您也可以更改它以获取状态。
SELECT
job.name,
job.job_id,
job.originating_server,
activity.run_requested_date,
activity.stop_execution_date,
DATEDIFF( SECOND, activity.run_requested_date, activity.stop_execution_date ) as Elapsed
FROM msdb.dbo.sysjobs_view job
JOIN msdb.dbo.sysjobactivity activity ON job.job_id = activity.job_id
JOIN msdb.dbo.syssessions sess ON sess.session_id = activity.session_id
JOIN
(
SELECT
MAX( agent_start_date ) AS max_agent_start_date
FROM
msdb.dbo.syssessions
) sess_max
ON sess.agent_start_date = sess_max.max_agent_start_date
WHERE run_requested_date IS NOT NULL
--AND stop_execution_date IS NULL
AND job.name = @JobName
【讨论】:
【参考方案4】:这是一个脚本,它将检查作业的状态,如果它尚未运行,则会运行它。
declare @xp_results table (
job_id UNIQUEIDENTIFIER NOT NULL,
last_run_date INT NOT NULL,
last_run_time INT NOT NULL,
next_run_date INT NOT NULL,
next_run_time INT NOT NULL,
next_run_schedule_id INT NOT NULL,
requested_to_run INT NOT NULL, -- BOOL
request_source INT NOT NULL,
request_source_id sysname COLLATE database_default NULL,
running INT NOT NULL, -- BOOL
current_step INT NOT NULL,
current_retry_attempt INT NOT NULL,
job_state INT NOT NULL)
DECLARE @job_id uniqueidentifier ;
select @job_id = job_id from msdb.dbo.sysjobs where name = 'Job1';
insert into @xp_results
EXEC master.dbo.xp_sqlagent_enum_jobs 1, sa, @job_id
select case when running = 1 then 'Currently Running' else '' end as running,
case job_state
when 0 then 'Not Idle or Suspended'
when 1 then 'Executing Job'
when 2 then 'Waiting For Thread'
when 3 then 'Between Retries'
when 4 then 'Idle'
when 5 then 'Suspended'
when 6 then 'WaitingForStepToFinish'
when 7 then 'PerformingCompletionActions'
end as job_state
from @xp_results
IF (select running from @xp_results) <> 1
EXEC msdb.dbo.sp_start_job 'Job1'
【讨论】:
【参考方案5】:@lapponiandevil 在这里有最好、最有用的解决方案,但是他们的代码比它需要的稍微复杂一些,并且实际上并不能按原样工作,因为它需要 @Time_constraint 和 @ok 变量,这些变量在显示代码。
这些是为了支持代理作业的超时,但这不是必需的。代理作业步骤可以配置有自己的超时值,如果超过它会正确出错,这与他们的代码不同。如果您使用该超时方法,您可能会发现自己正在寻找幻像错误,或者在您进入流程的下一步时不知道您正在等待的代理作业仍在运行。
我认为将代码减少到最低限度并删除超时功能是理想的。这是我根据他们的解决方案提出的:
-- Start Agent Job
DECLARE @JobName NVARCHAR(128) = 'My Agent Job Name'
EXEC msdb.dbo.sp_start_job @JobName
-- Wait for Agent Job to finish
DECLARE @HistoryID AS INT = NULL
WHILE @HistoryID IS NULL
BEGIN
--Read most recent Job History ID for the specified Agent Job name
SELECT TOP 1 @HistoryID = b.job_history_id
FROM msdb.dbo.sysjobs a
INNER JOIN msdb.dbo.sysjobactivity b ON b.job_id = a.job_id
WHERE a.name = @JobName
ORDER BY b.Start_execution_date DESC
--If Job is still running (Job History ID = NULL), wait 3 seconds
IF @HistoryID IS NULL WAITFOR DELAY '00:00:03'
END
-- Check Agent Job exit code to make sure it succeeded
IF (SELECT run_status
FROM msdb.dbo.sysjobhistory
WHERE instance_id = @HistoryID) <> 1
THROW 69000, 'Child Agent Job failure', 1;
【讨论】:
这对我有帮助 - 我只有一个注释:我必须将IF... DELAY...
语句移动到块的顶部,就在“BEGIN ...”下方,以便它在选择之前等待第一次。否则,在第一次迭代时,系统信息尚未创建,因此它从之前的运行中获取 stop_execution_date,因此永远不会等待。我会马上自己编辑,但有些作者不喜欢这样以上是关于从存储过程执行 SQL Server 代理作业并返回作业结果的主要内容,如果未能解决你的问题,请参考以下文章
使用 ssis 包在 SQL Server 代理作业中找不到存储过程错误
sql server 2008,SP_OACreate手动执行成功,代理作业定时执行失败?