我有一个 SQL Server 代理作业,旨在重新配置自身以响应作业启动时的条件。
因此,本质上,第一步更新了第二步。这种情况是正确的,但 SQL Server 代理进程似乎不知道第二步已更新,并运行了步骤 2 命令的旧缓存副本。我尝试添加sp_update_job
到第一步,试图让代理进程更新其缓存,但并不高兴。
这是一个mcve供您欣赏:
BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
END
DECLARE @jobId BINARY(16)
EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'Test self-updating job',
@enabled=1,
@notify_level_eventlog=0,
@notify_level_email=0,
@notify_level_netsend=0,
@notify_level_page=0,
@delete_level=0,
@description=N'No description available.',
@category_name=N'[Uncategorized (Local)]',
@owner_login_name=N'sa', @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'step 1 - update step 2',
@step_id=1,
@cmdexec_success_code=0,
@on_success_action=3,
@on_success_step_id=0,
@on_fail_action=2,
@on_fail_step_id=0,
@retry_attempts=0,
@retry_interval=0,
@os_run_priority=0, @subsystem=N'TSQL',
@command=N'USE [msdb];
GO
DECLARE @job_name sysname = N''Test self-updating job'';
DECLARE @command nvarchar(max);
IF EXISTS
(
SELECT 1
FROM [msdb].[dbo].[sysjobsteps] [sjs]
INNER JOIN [msdb].[dbo].[sysjobs] [sj] ON [sjs].[job_id] = [sj].[job_id]
WHERE [sj].[name] = N''Test self-updating job''
AND [sjs].[step_id] = 2
AND [sjs].[command] = N''PRINT N''''A'''';''
)
BEGIN
SET @command = N''PRINT N''''B'''';'';
END
ELSE
BEGIN
SET @command = N''PRINT N''''A'''';'';
END;
EXEC [msdb].[dbo].[sp_update_jobstep] @job_name = @job_name, @step_id = 2, @command = @command;
EXEC [msdb].[dbo].[sp_update_job] @job_name = @job_name, @automatic_post = 1;
',
@database_name=N'master',
@flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'step 2 - do the work',
@step_id=2,
@cmdexec_success_code=0,
@on_success_action=1,
@on_success_step_id=0,
@on_fail_action=2,
@on_fail_step_id=0,
@retry_attempts=0,
@retry_interval=0,
@os_run_priority=0, @subsystem=N'TSQL',
@command=N'PRINT N''A'';',
@database_name=N'master',
@flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave:
GO
测试作业从第二步开始,在打印A
或B
每次运行之间交替。即第一次运行时,应该打印B
,然后下次运行时应该打印A
,然后下次运行时,应该打印,B
依此类推。
运行作业并检查作业历史记录显示输出为A
,但是检查第二步的命令显示输出为PRINT N'B';
。因此,步骤命令正在更新,但代理实际上正在运行旧的预更新步骤代码。
有没有办法让 SQL Server 代理运行步骤 2 中更新的代码而不是旧的过时代码?
在实际工作中,第二步是使用代理运行的 cmdExec 命令,因此我不能仅在一步中使用动态 SQL。
解决这个问题最直接的方法是将作业的阶段分为两个作业 - 一个作业决定需要完成哪些工作,第二个作业根据工作的定义进行更新。第一个作业响应警报或计划并更新第二个作业中的步骤定义,然后启动第二个作业来执行实际工作。
暂时离开 SQL Server 代理作业的装配,考虑两个作业:
Test self-updating job - part 1
Test self-updating job - part 2
第一个作业的步骤 1 具有以下 T-SQL:
作业 2 将其作为步骤 1 的初始内容:
当第一个作业运行时,它会切换作业 2 中的命令内容,然后再使用 启动它
sp_start_job
。