从存储过程执行SQL Server代理作业并返回作业结果

问题描述 投票:0回答:6

需要有一个存储过程来调用 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

哪个启动作业,如果作业运行成功或失败,返回的最佳方式是什么?

好的,进行了编辑并使用了 WAITFOR DELAY,因为该作业通常运行 3-4 分钟,但绝不会超过 4 分钟。该作业是否可以完成,但有更有效的方法吗?

sql sql-server sql-server-2008 t-sql jobs
6个回答
23
投票

对于所有不允许使用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


21
投票

您可以运行查询:

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,我们就知道工作已完成,并且可以安全地检查工作历史记录。


2
投票

这是一个脚本,用于检查作业的状态,如果尚未运行则运行它。

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'

1
投票

@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;  

0
投票

我可能有点晚了,但我发现以下查询对我有用。它将给出执行时间和执行结束时间。您也可以更改它以获得状态。

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

0
投票

我认为 SQL Server 不提供任何硬 ID 或对您执行的作业的特定实例的引用是荒谬的。因此,我们希望,如果我们根据执行时间过滤作业步骤历史记录,它将选择正确的作业实例,祈祷我们能够通过魔法避免数据竞争。对于价值十亿美元的数据产品来说,这实际上是疯狂的。

无论如何,这是一个按名称执行作业的存储过程,使用

sysjobactivity
+
sysjobhistory
等待指定的秒数以使其完成。

CREATE procedure [dbo].[StartAgentJobAndWait] @jobName nvarchar(128), @maxWaitSeconds int = 360
/*
Raises an exception if Job @jobName does not exist.
Returns int:
    -2 = job did not complete before @maxWaitSeconds
    -1 = failed to start
     0 = Failed
     1 = Succeeded
     2 = Retry
     3 = Canceled
     4 = In Progress 
*/
AS BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;

    DECLARE @start     DATETIME         = DATETRUNC(second,GETDATE())
    ,       @jobId     UNIQUEIDENTIFIER = (SELECT TOP 1 job_id FROM msdb..sysjobs WHERE NAME = @jobName)
    ,       @runStatus INT

    /* error if invalid job name */
    IF @jobId IS NULL RAISERROR (N'Unknown job: %s.', 16, 1, @jobName);
    
    /* start the job */
    EXEC @runStatus = msdb..sp_start_job @job_id = @jobId;
    
    /* job failed to start */
    IF @runStatus = 1 RETURN -1;

    WHILE (DATEDIFF(second, @start, GETDATE()) < @maxWaitSeconds)
    BEGIN
        SELECT TOP 1 @runStatus = run_status
        FROM msdb..sysjobactivity
        LEFT JOIN msdb..sysjobhistory ON sysjobactivity.job_history_id = sysjobhistory.instance_id AND step_id = 0
        WHERE sysjobactivity.job_id = @jobId AND run_requested_date >= @start
        ORDER BY run_requested_date ASC

        IF @runStatus IS NOT NULL
        BEGIN
            PRINT 'Job completed in '+CONVERT(VARCHAR(20),DATEDIFF(second, @start, GETDATE()))+' seconds with run_status = '+CONVERT(VARCHAR(20),@runStatus)
            BREAK
        END

        PRINT 'Job is still running after '+CONVERT(VARCHAR(20),DATEDIFF(second, @start, GETDATE()))+' seconds...'
        WAITFOR DELAY '0:0:05';
    END

    IF @runStatus IS NULL
    BEGIN
        PRINT 'Job did not complete before '+CONVERT(VARCHAR(20),@maxWaitSeconds)+' seconds elapsed.'
        SET @runStatus = -2
    END

    RETURN @runStatus
END
© www.soinside.com 2019 - 2024. All rights reserved.