TRY / CATCH无法解决SQL Server代理错误?

时间:2013-03-27 12:37:59

标签: sql-server try-catch sql-server-agent

我使用sp_start_job开始工作。

作业(test2)只有一步:

select getdate()
waitfor delay '00:00:10'

TRY/CATCH代码:

begin try
    EXEC msdb.dbo.sp_start_job @job_name = 'test2'
end try
begin catch
    print 'error'
end catch

首次运行代码:

  

Job'test2'成功启动。

第二次运行代码(10秒内):

  

消息22022,级别16,状态1,行0
SQLServerAgent错误:请求运行作业test2(来自用户sa)被拒绝,因为作业已经从用户sa的请求运行。

为什么TRY/CATCH在这种情况下不起作用?

更新: 我应该首先添加我正在使用链接服务器(sql server 2000)的sql server 2005。我试图在sql server 2005服务器上编写一个proc来查看所有链接服务器上的作业。如果作业未运行,请运行它。最初,我使用try-catch并希望在运行已经运行的作业时捕获任何错误但是失败了(这个线程)。

我终于使用了以下代码:(它不会编译,你需要替换一些变量,只是给出一个想法)

    CREATE TABLE [dbo].[#jobInfo](
        [job_id] [uniqueidentifier] NULL,
        [originating_server] [nvarchar](30) ,
        [name] [nvarchar](128) ,
        [enabled] [tinyint] NULL,
        [description] [nvarchar](512) ,
        [start_step_id] [int] NULL,
        [category] [nvarchar](128) ,
        [owner] [nvarchar](128) ,
        [notify_level_eventlog] [int] NULL,
        [notify_level_email] [int] NULL,
        [notify_level_netsend] [int] NULL,
        [notify_level_page] [int] NULL,
        [notify_email_operator] [nvarchar](128) ,
        [notify_netsend_operator] [nvarchar](128) ,
        [notify_page_operator] [nvarchar](128) ,
        [delete_level] [int] NULL,
        [date_created] [datetime] NULL,
        [date_modified] [datetime] NULL,
        [version_number] [int] NULL,
        [last_run_date] [int] NOT NULL,
        [last_run_time] [int] NOT NULL,
        [last_run_outcome] [int] NOT NULL,
        [next_run_date] [int] NOT NULL,
        [next_run_time] [int] NOT NULL,
        [next_run_schedule_id] [int] NOT NULL,
        [current_execution_status] [int] NOT NULL,
        [current_execution_step] [nvarchar](128) ,
        [current_retry_attempt] [int] NOT NULL,
        [has_step] [int] NULL,
        [has_schedule] [int] NULL,
        [has_target] [int] NULL,
        [type] [int] NOT NULL
    )


    SET @sql = 
    'INSERT INTO #jobInfo
    SELECT * FROM OPENQUERY( [' + @srvName + '],''set fmtonly off exec msdb.dbo.sp_help_job'')'

    EXEC(@sql)

    IF EXISTS (select * from #jobInfo WHERE [name] = @jobName AND current_execution_status IN (4,5)) -- 4: idle, 5: suspended 
    BEGIN
        SET @sql = 'EXEC [' + @srvName + '].msdb.dbo.sp_start_job @job_name = ''' + @jobName + ''''
        --print @sql    
        EXEC (@sql) 
        INSERT INTO #result (srvName ,status ) VALUES (@srvName, 'Job started.')
    END ELSE BEGIN
        INSERT INTO #result (srvName ,status ) VALUES (@srvName, 'Job is running already. No action taken.')
    END

4 个答案:

答案 0 :(得分:6)

TRY/CATCH并非所有错误都可以捕获。在这种情况下,sp_start_job实际上调用了外部过程,这些过程超出了SQL Server错误处理的范围。或者至少那是他们坚持的故事:

http://connect.microsoft.com/SQLServer/feedback/details/362112/sp-start-job-error-handling

另请注意,这仍是SQL Server 2012 SP1 CU3中的问题。 如果您想修复此错误,请投票和评论。

繁琐但可行的解决方法,需要特定权限,在这种情况下假定作业所有者为sa

DECLARE @x TABLE
(
  a VARBINARY(32),b INT,c INT,d INT,e INT,f INT,g INT,h INT,i NVARCHAR(64),
  Running BIT, -- the only important column
  k INT,l INT,m INT
);

DECLARE @job_id UNIQUEIDENTIFIER;

SELECT @job_id = job_id FROM msdb.dbo.sysjobs WHERE name = N'test2';

INSERT @x EXEC master.dbo.xp_sqlagent_enum_jobs 1, N'sa', @job_id;

IF EXISTS (SELECT 1 FROM @x WHERE Running = 0)
BEGIN
     EXEC msdb.dbo.sp_start_job @job_name = N'test2';
END
ELSE
BEGIN
     PRINT 'error';
END

更好的可能是:

DECLARE @job_id UNIQUEIDENTIFIER, @d DATETIME;

SELECT @job_id = job_id FROM msdb.dbo.sysjobs WHERE name = N'test2';

SELECT @d = stop_execution_date 
  FROM msdb.dbo.sysjobactivity WHERE job_id = @job_id;

IF @d IS NOT NULL
BEGIN
     EXEC msdb.dbo.sp_start_job @job_name = N'test2';
END
ELSE
BEGIN
     PRINT 'error';
END

在任何一种情况下,作业仍然可能在检查其状态和启动它的调用之间开始,因此这不会完全消除sp_start_job的错误,但这会使它们更少可能会发生。

答案 1 :(得分:0)

您可以使用警报来执行SQL Agent作业。而且,您可以使用警报的选项来配置所需的响应。 (这将帮助您完全避免错误22022,但错误日志中将包含其他记录)

答案 2 :(得分:0)

这是捕获作业触发故障的方法。

声明@returnstatus int

Exec @returnstatus msdb.dbo.sp_start_job'职位名称'

如果(@returnstaus = 1) 打印“成功” 其他 打印“失败”

答案 3 :(得分:0)

要做出常青解决方案,我们还应该考虑以前从未运行过的作业。 这在重建 SQL 服务器和重新创建作业的场景中很有用。 要捕获该场景,请查看上次运行请求的日期:

DECLARE @jobEnd DATETIME, @jobRun DATETIME
SELECT @jobEnd = sja.stop_execution_date , @jobRun = sja.run_requested_date
FROM msdb.dbo.sysjobactivity AS sja
INNER JOIN msdb.dbo.sysjobs AS sj
ON sja.job_id = sj.job_id
WHERE sj.name = 'PhoneListSyncMember'

IF (@jobEnd IS NOT NULL AND @jobRun IS NOT NULL)
  OR (@jobEnd IS NULL AND @jobRun IS NULL)  -- job is New and never run before
    EXEC @iRet =msdb.dbo.sp_start_job @job_name='PhoneListSyncMember'