【问题标题】:TRY/CATCH does not work on SQL Server Agent error?TRY/CATCH 对 SQL Server 代理错误不起作用?
【发布时间】:2013-03-27 12:37:59
【问题描述】:

我使用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

第一次运行代码:

作业“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

【问题讨论】:

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


    【解决方案1】:

    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 的错误,但它使它们不太可能发生发生。

    【讨论】:

    • 如果没有 INSERT @x,那么它可以工作。但是当使用 INSERT @x 时,我得到了
    • 如果您在插入@x 时遇到错误,您可能运行的版本与我不同。查看 XP 的列输出;您可能必须删除一两列或修复数据类型(我不知道您遇到了什么错误)。我没有一个方便的清单来说明这个 XP 的界面在不同版本之间是如何变化的。无论如何,正如我所说,第二个代码示例可能更好。
    • 我的意思是我从本地服务器 (2005) 在链接服务器 (SQL Server 2000) 上运行这些,所以语句实际上是 [linkedServer].master.dbo.xp_sqlagent_enum_jobs,我得到了以下错误链接服务器“hp-dublin”的 OLE DB 提供程序“SQLNCLI”返回消息“合作伙伴事务管理器已禁用其对远程/网络事务的支持。”。消息 7391,级别 16,状态 2,第 12 行操作无法执行,因为链接服务器“hp-dublin”的 OLE DB 提供程序“SQLNCLI”无法开始分布式事务。
    • 如果没有 INSERT @x,它运行正常
    • 还有更多新信息 - SQL Server 2000? 耸耸肩 你可能不得不在别处处理这个问题。您正在更改所有要求,并且添加的每个变量都使解决问题变得更加困难。今后请预先说明所有这些额外要求。
    【解决方案2】:

    您可以使用警报来执行 SQL 代理作业。您可以使用警报选项来配置所需的响应。 (这将帮助您完全避免错误 22022,但您将在错误日志中记录额外的记录)

    【讨论】:

    • 你在说什么“警报”?
    • 您尝试使用 sp_start_job 执行作业。但是可以使用警报来做到这一点。您可以创建警报(sp_add_alert),将您的工作指定为警报的“响应”,然后以任何方式激活该警报(raiserror 就足够了)。并注意警报的“延迟”选项。通过这种方式,您可以获得“替代”工作激活。
    • 使用 RAISERROR,您需要 WITH LOG 才能使其工作,这需要 sa 级权限,与运行 sp_start_job 所需的权限不同。在我的测试中,不需要延迟,因为代理只会使用串行作业处理警报。绝招!
    • create login test with password = '1', check_policy = off go execute as login = 'test' go raiserror( '1', 1, 1 ) with log go revert go create proc proc_delme as raiserror ('1', 1, 1) with log go create user test from login test with default_schema = dbo go grant 在 proc_delme 上执行以测试 go 执行为 login = 'test' go exec proc_delme go revert go drop user test go drop login test去
    • 抱歉,您是对的,但是您可以使用该“raiserror”创建程序并且可以正常工作。我尝试在上面的评论中添加示例代码。
    【解决方案3】:

    以下是捕获作业触发失败的方法。

    声明@returnstatus int

    执行@returnstatus msdb.dbo.sp_start_job '作业名'

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

    【讨论】:

      【解决方案4】:

      要制定一个常青的解决方案,我们还应该考虑一个以前从未运行过的工作。 这在重建 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'
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-01
        • 1970-01-01
        • 2012-09-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多