Respuesta corta
Parece que los datos msdb.dbo.sysjobschedules
se actualizan mediante un subproceso en segundo plano en el Agente SQL, identificado como SQLAgent - Schedule Saver
, cada 20 minutos (o con menos frecuencia, si xp_sqlagent_notify
no se ha llamado y mientras tanto no se han ejecutado trabajos).
Para obtener información más precisa, mira next_scheduled_run_date
en msdb.dbo.sysjobactivity
. Esto se actualiza en tiempo real cada vez que se cambia un trabajo o se ejecuta un trabajo. Como una ventaja adicional, sysjobactivity
almacena los datos de la manera correcta (como una columna de fecha y hora), lo que hace que sea mucho más fácil trabajar con esos estúpidos INT.
Esa es la respuesta corta:
Pueden pasar hasta 20 minutos antes de que sysjobschedules refleje la verdad; sin embargo, la sysjobactivity siempre estará actualizada. Si quieres muchos más detalles sobre esto, o cómo lo descubrí ...
Respuesta larga
Si te interesa seguir al conejo por un momento, cuando llamas sp_add_jobschedule
, esta cadena de eventos se pone en marcha:
msdb.dbo.sp_add_jobschedule == calls ==> msdb.dbo.sp_add_schedule
msdb.dbo.sp_attach_schedule
msdb.dbo.sp_attach_schedule == calls ==> msdb.dbo.sp_sqlagent_notify
msdb.dbo.sp_sqlagent_notify == calls ==> msdb.dbo.xp_sqlagent_notify
Ahora, no podemos perseguir al conejo más, porque realmente no podemos echar un vistazo a lo que xp_sqlagent_notify
hace. Pero creo que podemos suponer que este procedimiento extendido interactúa con el servicio del Agente y le dice que ha habido un cambio en este trabajo y horario específicos. Al ejecutar un rastreo del lado del servidor, podemos ver que, de inmediato, el Agente SQL llama al siguiente SQL dinámico:
exec sp_executesql N'DECLARE @nextScheduledRunDate DATETIME
SET @nextScheduledRunDate = msdb.dbo.agent_datetime(@P1, @P2)
UPDATE msdb.dbo.sysjobactivity
SET next_scheduled_run_date = @nextScheduledRunDate
WHERE session_id = @P3 AND job_id = @P4',
N'@P1 int,@P2 int,@P3 int,@P4 uniqueidentifier',
20120819,181600,5,'36924B24-9706-4FD7-8B3A-1F9F0BECB52C'
Parece que sysjobactivity
se actualiza de inmediato y sysjobschedules
solo se actualiza en un horario. Si cambiamos el nuevo horario para que sea una vez al día, por ej.
@freq_type=4,
@freq_interval=1,
@freq_subday_type=1,
@freq_subday_interval=0,
@freq_relative_interval=0,
@freq_recurrence_factor=1,
Todavía vemos la actualización inmediata a la sysjobactivity
anterior, y luego otra actualización una vez que finaliza el trabajo. Varias actualizaciones provienen de fondo y otros hilos dentro del Agente SQL, por ejemplo:
SQLAgent - Job Manager
SQLAgent - Update job activity
SQLAgent - Job invocation engine
SQLAgent - Schedule Saver
Un subproceso de fondo (el subproceso "Ahorro de horario") aparece y se actualiza sysjobschedules
; de mi investigación inicial parece que esto es cada 20 minutos, y solo sucede si xp_sqlagent_notify
se ha llamado debido a un cambio realizado en un trabajo desde la última vez que se ejecutó (no realicé ninguna prueba adicional para ver qué sucede si se ha realizado un trabajo cambiado y se ha ejecutado otro, si el hilo "Schedule Saver" actualiza ambos, sospecho que debe hacerlo, pero lo dejaré como un ejercicio para el lector).
No estoy seguro de si el ciclo de 20 minutos está compensado desde el inicio del Agente SQL, o desde la medianoche, o desde algo específico de la máquina. En dos instancias diferentes en el mismo servidor físico, el hilo "Schedule Saver" se actualizó sysjobschedules
, en ambas instancias, casi al mismo tiempo: 18:31:37 y 18:51:37 en una, y 18:31:39 y 18:51:39 por el otro. No inicié el Agente SQL Server al mismo tiempo en estos servidores, pero existe la posibilidad remota de que las horas de inicio sean 20 minutos de compensación. Lo dudo, pero no tengo tiempo en este momento para confirmar reiniciando el Agente en uno de ellos y esperando que ocurran más actualizaciones.
Sé quién lo hizo, y cuándo sucedió, porque puse un gatillo allí y lo capturé, en caso de que no pudiera encontrarlo en el rastro, o lo filté inadvertidamente.
CREATE TABLE dbo.JobAudit
(
[action] CHAR(1),
[table] CHAR(1),
hostname SYSNAME NOT NULL DEFAULT HOST_NAME(),
appname SYSNAME NOT NULL DEFAULT PROGRAM_NAME(),
dt DATETIME2 NOT NULL DEFAULT SYSDATETIME()
);
CREATE TRIGGER dbo.schedule1 ON dbo.sysjobactivity FOR INSERT
AS
INSERT dbo.JobAudit([action], [table] SELECT 'I', 'A';
GO
CREATE TRIGGER dbo.schedule2 ON dbo.sysjobactivity FOR UPDATE
AS
INSERT dbo.JobAudit([action], [table] SELECT 'U', 'A';
GO
CREATE TRIGGER dbo.schedule3 ON dbo.sysjobschedules FOR INSERT
AS
INSERT dbo.JobAudit([action], [table] SELECT 'I', 'S';
GO
CREATE TRIGGER dbo.schedule4 ON dbo.sysjobschedules FOR UPDATE
AS
INSERT dbo.JobAudit([action], [table] SELECT 'U', 'S';
GO
Dicho esto, no es difícil atrapar con un rastreo estándar, este incluso se presenta como DML no dinámico:
UPDATE msdb.dbo.sysjobschedules
SET next_run_date = 20120817,
next_run_time = 20000
WHERE (job_id = 0xB87B329BFBF7BA40B30D9B27E0B120DE
and schedule_id = 8)
Si desea ejecutar una traza más filtrada para rastrear este comportamiento a lo largo del tiempo (por ejemplo, persistir a través del reinicio del Agente SQL en lugar de bajo demanda), puede ejecutar uno que tenga appname = 'SQLAgent - Schedule Saver'
...
Así que creo que si quieres saber el próximo tiempo de ejecución inmediatamente, mira sysjobactivity
, no sysjobschedules
. Esta tabla es actualizada directamente por el Agente o sus subprocesos en segundo plano ("Actualizar actividad de trabajo", "Administrador de trabajos" y "Motor de invocación de trabajos") a medida que ocurre la actividad o según lo notifica xp_sqlagent_notify
.
Sin embargo, tenga en cuenta que es muy fácil eliminar cualquier tabla, ya que no hay protecciones contra la eliminación de datos de estas tablas. (Entonces, si decidió limpiar, por ejemplo, puede eliminar fácilmente todas las filas para ese trabajo de la tabla de actividades). En este caso, no estoy exactamente seguro de cómo el Agente SQL Server obtiene o guarda la próxima fecha de ejecución. Quizás merezca más investigación en una fecha posterior cuando tenga algo de tiempo libre ...