Eu tenho um trabalho do SQL Server Agent projetado para se reconfigurar em resposta às condições existentes quando o trabalho é iniciado.
Então, em essência, a primeira etapa atualiza a segunda etapa. Isso acontece corretamente, mas parece que o processo do SQL Server Agent não sabe que a segunda etapa foi atualizada e executa a cópia antiga em cache do comando da etapa 2. Tentei adicionar sp_update_job
a primeira etapa na tentativa de fazer com que o processo do Agente atualizasse seu cache, mas não foi uma alegria.
Aqui está um mcve para seu prazer:
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
O trabalho de teste começa com a 2ª etapa alternando entre a impressão A
ou B
cada execução. ou seja, na primeira vez que for executado, ele deverá imprimir B
, na próxima vez que for executado, deverá imprimir A
, na próxima vez que for executado, deverá imprimir B
e assim por diante.
Executar o trabalho e verificar o histórico do trabalho mostra que a saída é A
, porém a inspeção do comando para a segunda etapa mostra que é PRINT N'B';
. Portanto, o comando step está sendo atualizado, mas o Agente está, na verdade, executando o antigo código de etapa pré-atualizado.
Existe uma maneira de fazer com que o SQL Server Agent execute o código atualizado na etapa 2 em vez do código antigo e desatualizado?
No trabalho real, a segunda etapa é o comando cmdExec que é executado usando um proxy, portanto não posso usar SQL dinâmico em uma única etapa.
A maneira mais direta de contornar esse problema é separar os estágios do trabalho em dois trabalhos – um trabalho que decide qual trabalho precisa ser feito e um segundo trabalho que é atualizado com a definição do trabalho. O primeiro trabalho responde ao alerta ou agendamento e atualiza a definição das etapas no segundo trabalho e, em seguida, aciona esse segundo trabalho para realizar o trabalho real.
Afastando-se da preparação de um trabalho de agente do SQL Server por um minuto, considere dois trabalhos:
Test self-updating job - part 1
Test self-updating job - part 2
A etapa 1 do primeiro trabalho possui o seguinte T-SQL:
O trabalho 2 tem este como conteúdo inicial da etapa 1:
Quando o primeiro trabalho é executado, ele alterna o conteúdo do comando no trabalho 2, antes de iniciá-lo com
sp_start_job
.