我一直在尝试使用 SQL Server 2005 查找示例代码,我需要启动一个代理作业并等到它完成过程然后获得成功或失败。
我知道我可以使用
EXEC MSDB.dbo.sp_start_job @Job_Name = 'JobName'
开始工作,但我找不到任何示例代码来轮询msdb.dbo.sp_help_job
以了解它何时完成,然后找出它是成功还是失败。
我一直在尝试使用 SQL Server 2005 查找示例代码,我需要启动一个代理作业并等到它完成过程然后获得成功或失败。
我知道我可以使用
EXEC MSDB.dbo.sp_start_job @Job_Name = 'JobName'
开始工作,但我找不到任何示例代码来轮询msdb.dbo.sp_help_job
以了解它何时完成,然后找出它是成功还是失败。
-- =============================================
-- Description: Starts a SQLAgent Job and waits for it to finish or until a specified wait period elapsed
-- @result: 1 -> OK
-- 0 -> still running after maxwaitmins
-- =============================================
CREATE procedure [dbo].[StartAgentJobAndWait](@job nvarchar(128), @maxwaitmins int = 5) --, @result int output)
AS
BEGIN
set NOCOUNT ON;
set XACT_ABORT ON;
BEGIN TRY
declare @running as int
declare @seccount as int
declare @maxseccount as int
declare @start_job as bigint
declare @run_status as int
set @start_job = cast(convert(varchar, getdate(), 112) as bigint) * 1000000 + datepart(hour, getdate()) * 10000 + datepart(minute, getdate()) * 100 + datepart(second, getdate())
set @maxseccount = 60*@maxwaitmins
set @seccount = 0
set @running = 0
declare @job_owner sysname
declare @job_id UNIQUEIDENTIFIER
set @job_owner = SUSER_SNAME()
-- get job id
select @job_id=job_id
from msdb.dbo.sysjobs sj
where sj.name=@job
-- invalid job name then exit with an error
if @job_id is null
RAISERROR (N'Unknown job: %s.', 16, 1, @job)
-- output from stored procedure xp_sqlagent_enum_jobs is captured in the following table
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)
-- start the job
declare @r as int
exec @r = msdb..sp_start_job @job
-- quit if unable to start
if @r<>0
RAISERROR (N'Could not start job: %s.', 16, 2, @job)
-- start with an initial delay to allow the job to appear in the job list (maybe I am missing something ?)
WAITFOR DELAY '0:0:01';
set @seccount = 1
-- check job run state
insert into @xp_results
execute master.dbo.xp_sqlagent_enum_jobs 1, @job_owner, @job_id
set @running= (SELECT top 1 running from @xp_results)
while @running<>0 and @seccount < @maxseccount
begin
WAITFOR DELAY '0:0:01';
set @seccount = @seccount + 1
delete from @xp_results
insert into @xp_results
execute master.dbo.xp_sqlagent_enum_jobs 1, @job_owner, @job_id
set @running= (SELECT top 1 running from @xp_results)
end
-- result: not ok (=1) if still running
if @running <> 0 begin
-- still running
return 0
end
else begin
-- did it finish ok ?
set @run_status = 0
select @run_status=run_status
from msdb.dbo.sysjobhistory
where job_id=@job_id
and cast(run_date as bigint) * 1000000 + run_time >= @start_job
if @run_status=1
return 1 --finished ok
else --error
RAISERROR (N'job %s did not finish successfully.', 16, 2, @job)
end
END TRY
BEGIN CATCH
DECLARE
@ErrorMessage NVARCHAR(4000),
@ErrorNumber INT,
@ErrorSeverity INT,
@ErrorState INT,
@ErrorLine INT,
@ErrorProcedure NVARCHAR(200);
SELECT
@ErrorNumber = ERROR_NUMBER(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorLine = ERROR_LINE(),
@ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
SELECT @ErrorMessage =
N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' +
'Message: '+ ERROR_MESSAGE();
RAISERROR
(
@ErrorMessage,
@ErrorSeverity,
1,
@ErrorNumber, -- original error number.
@ErrorSeverity, -- original error severity.
@ErrorState, -- original error state.
@ErrorProcedure, -- original error procedure name.
@ErrorLine -- original error line number.
);
END CATCH
END
您可以查阅 sysjobhistory 表中的run_status列。0 表示失败。
Here is code that I wrote for this purpose. One caveat is that it does not handle the case where the job is already running when this procedure is executed.
CREATE PROCEDURE [admin].[StartAgentJobAndWaitForCompletion]
@JobName SYSNAME,
@TimeLimit INT = 60, -- Stop waiting after this number of minutes
@Debug BIT = 0
AS
SET NOCOUNT ON;
DECLARE @JobId UNIQUEIDENTIFIER,
@Current DATETIME,
@Message NVARCHAR(MAX),
@SessionId INT;
SELECT @JobId = job_id
FROM msdb.dbo.sysjobs
WHERE name = @JobName;
IF @JobId IS NULL BEGIN
RAISERROR ('No job named "%s"', 16, 1, @JobName) WITH NOWAIT;
RETURN 1;
END;
EXEC msdb.dbo.sp_start_job @job_id = @JobId;
IF @Debug =1 BEGIN
SET @Message = CONVERT(VARCHAR(19), CURRENT_TIMESTAMP, 120) + ' ' + @JobName + ' started';
RAISERROR (@Message, 0, 1) WITH NOWAIT;
END;
SET @Current = CURRENT_TIMESTAMP;
WAITFOR DELAY '00:00:02'; -- Allow time for the system views to be populated
WHILE DATEADD(mi, @TimeLimit, @Current) > CURRENT_TIMESTAMP BEGIN
SET @SessionId = NULL;
SELECT TOP(1) @SessionId = session_id
FROM msdb.dbo.sysjobactivity sja
WHERE sja.job_id = @JobId
AND sja.start_execution_date IS NOT NULL
AND sja.stop_execution_date IS NULL
ORDER BY sja.start_execution_date DESC;
IF @SessionId IS NULL
BREAK;
IF @Debug =1 BEGIN
SET @Message = CONVERT(VARCHAR(19), CURRENT_TIMESTAMP, 120) + ' ' + @JobName + ', Session: ' + CONVERT(VARCHAR(38), @SessionId);
RAISERROR (@Message, 0, 1) WITH NOWAIT;
END;
WAITFOR DELAY '00:00:05';
END;
IF @Debug = 1 BEGIN
SET @Message = CONVERT(VARCHAR(19), CURRENT_TIMESTAMP, 120) + ' ' + @JobName + ' completed';
RAISERROR (@Message, 0, 1) WITH NOWAIT;
END;
WAITFOR DELAY '00:00:02'; -- Allow time for the system views to be populated
RETURN 0;
也许不是一个非常可靠的方法,但我可能会尝试让作业在进程开始和结束时写入某个表,并在我的客户端应用程序中轮询该表(或使用 ADO 事件来触发相应的事件处理程序)。
CREATE PROCEDURE dbo.usp_RunJobWithOutcome
@JobName sysname
, @RunTimeout int
, @RunStatus int output
AS
SET NOCOUNT ON
--Verify that this job exists
IF NOT EXISTS (SELECT 1 FROM msdb.dbo.sysjobs WHERE [name] = @JobName)
BEGIN
SET @RunStatus = 5 --Unknown
RAISERROR('Invalid job name ''%s''', 16, 245, @JobName);
RETURN 1
END;
--Start the job
DECLARE @retval int;
exec @retval = msdb.dbo.sp_start_job @job_name=@JobName;
--If start succeeded, poll for completion
IF @retval = 0
BEGIN
PRINT N'Job started successfully';
WAITFOR DELAY '00:00:05';
DECLARE @JobRunTime int;
SET @JobRunTime = 0;
SET @RunStatus = 5; --Unknown -> default return
WHILE @JobRunTime < @RunTimeout
BEGIN
WAITFOR DELAY '00:00:05';
--SELECT statement below give the same result as 'sp_help_jobactivity' sys-proc
SELECT @JobRunTime = CASE WHEN stop_execution_date IS NULL THEN DATEDIFF(SECOND, start_execution_date, GETDATE()) ELSE @RunTimeout END
FROM (
SELECT ja.session_id, ja.job_id, j.[name] job_name, ja.run_requested_date, ja.run_requested_source, ja.queued_date, ja.start_execution_date
, ja.last_executed_step_id, ja.last_executed_step_date, ja.stop_execution_date, ja.next_scheduled_run_date, ja.job_history_id
, jh.[message], jh.run_status, jh.operator_id_emailed, jh.operator_id_netsent, jh.operator_id_paged
FROM msdb.dbo.sysjobactivity ja
JOIN msdb.dbo.sysjobs j ON ja.job_id = j.job_id
LEFT JOIN msdb.dbo.sysjobhistory jh ON ja.job_history_id = jh.instance_id
WHERE ja.session_id = (SELECT MAX(session_id) FROM msdb.dbo.sysjobactivity ja1 WHERE ja1.job_id = ja.job_id AND ja1.run_requested_date IS NOT NULL)
AND j.[name] = @JobName
) JobActivity;
END;
--Get the final stats
SELECT @RunStatus=run_status, @JobRunTime=DATEDIFF(SECOND, start_execution_date, stop_execution_date)
FROM (
SELECT ja.session_id, ja.job_id, j.[name] job_name, ja.run_requested_date, ja.run_requested_source, ja.queued_date, ja.start_execution_date
, ja.last_executed_step_id, ja.last_executed_step_date, ja.stop_execution_date, ja.next_scheduled_run_date, ja.job_history_id
, jh.[message], jh.run_status, jh.operator_id_emailed, jh.operator_id_netsent, jh.operator_id_paged
FROM msdb.dbo.sysjobactivity ja
JOIN msdb.dbo.sysjobs j ON ja.job_id = j.job_id
LEFT JOIN msdb.dbo.sysjobhistory jh ON ja.job_history_id = jh.instance_id
WHERE ja.session_id = (SELECT MAX(session_id) FROM msdb.dbo.sysjobactivity ja1 WHERE ja1.job_id = ja.job_id AND ja1.run_requested_date IS NOT NULL)
AND j.[name] = @JobName
) JobActivity;
PRINT N'Job completed in ' + CONVERT(nvarchar, @JobRunTime) + ' seconds.'
IF @RunStatus = 1 RETURN 0; --Success
ELSE RETURN 1; --Failed
END;
ELSE
BEGIN
PRINT N'Job could not start';
SET @RunStatus = 5 --Unknown
RETURN 1; --failed
END;
GO
DECLARE @RunStatus int, @retval int
--Run for max 60 minutes
exec @retval=dbo.usp_RunJobWithOutcome @JobName='*<your job name here>*', @RunTimeout=3600, @RunStatus=@RunStatus output
SELECT @retval, @RunStatus
GO