12

¿Puedo (cómo configuro) Sql Server 2008 notificar a un operador si falla algún paso en el trabajo?Notificar al operador si CUALQUIER paso en el trabajo falla

Tengo un trabajo de servidor Sql con varios pasos para actualizar los datos de varias fuentes diferentes, seguido de un paso final que realiza varios cálculos sobre los datos. Todos los pasos de "actualización de datos" están configurados en "Ir al siguiente paso en caso de fallo". En términos generales, si una de las actualizaciones de datos falla, todavía quiero que se ejecute el paso final, pero aún quiero que se me notifique sobre las fallas intermedias, por lo que si fallan de manera constante, puedo investigar.

Respuesta

24

Así es como lo hacemos. Añadimos un paso último de T-SQL (normalmente llamado "cheque pasos") con este Aviso

SELECT step_name, message 
FROM msdb.dbo.sysjobhistory 
WHERE instance_id > COALESCE((SELECT MAX(instance_id) FROM msdb.dbo.sysjobhistory 
           WHERE job_id = $(ESCAPE_SQUOTE(JOBID)) AND step_id = 0), 0) 
     AND job_id = $(ESCAPE_SQUOTE(JOBID)) 
     AND run_status <> 1 -- success 

IF  @@ROWCOUNT <> 0 
     RAISERROR('Ooops', 16, 1) 

que este código está utilizando tokens in job steps (la parte $(...)), por lo que el código no se puede ejecutar en SSMS como es. Básicamente trata de encontrar las entradas de los pasos anteriores del trabajo actual en sysjobhistory y busca los estados de falla.

En Propiedades-> Avanzado también puede marcar Incluir paso de salida en el historial para obtener el mensaje del error de paso. Deje En la acción de falla en Salga del error de informe de trabajo.

+0

Bonita e ingeniosa. Lo extendí a algo un poco menos bonito pero útil; ver mi respuesta – Adamantish

1

Ir a Propiedades del trabajo> ficha Notificación> acción a realizar cuando finalice la tarea

bajo esa marca la casilla de correo electrónico y seleccione "Cuando el trabajo falla" en el menú desplegable y guardar el trabajo.

Leer cuarto punto sobre http://msdn.microsoft.com/en-us/library/ms191130.aspx

Si desea notificar a un operador por e-mail, revisar el correo electrónico, seleccione un operador de la lista, y luego seleccione una de las siguientes:

  • Cuando el trabajo tiene éxito: para notificar al operador cuando el trabajo se completa con éxito.

  • Cuando falla la tarea: notificar al operador cuando el trabajo finaliza sin éxito.

  • Cuando el trabajo finaliza: para notificar al operador independientemente del estado de finalización.

+1

Desafortunadamente, esto no resuelve el problema. En primer lugar, enviará un correo electrónico una vez que se complete, independientemente de si tiene éxito o no. Esperaría ver correos electrónicos solo si un paso falla. - Además, el correo electrónico de finalización no incluye ningún texto que indique que un paso individual falló. Simplemente dice "El trabajo tuvo éxito". ¿Cómo puedo saber que un paso específico falló? –

+0

utilice esta opción "Cuando el trabajo falla" para notificar al operador cuando el trabajo finaliza sin éxito. Esto significa que recibirá una notificación si el trabajo se completó con pasos fallidos (completará el trabajo incluso si falla un paso, ya que el error va a el siguiente paso fue especificado para los pasos del trabajo). entonces puedes ver el historial de trabajo para más detalles ... ¡qué decir! – Thakur

+1

La opción de "Cuando el trabajo falla" no envía un correo electrónico porque el último paso fue exitoso (también conocido como "Salir del trabajo que informa el éxito") –

0

en cada paso de añadir código:

if @@error > 0 
EXEC sp_send_dbmail @profile_name='DBATeam', 
@[email protected]', 
@subject='SomeJob SomeStep failed', 
@body='This is the body of SomeJob SomeStep failed' 
1

tengo la mayoría de mis pasos establecidos para reintentar debido a un escenario translogarítmica único que provoca el bloqueo ocasional. La publicación de wqw alertará incluso si un paso se ha reintentado correctamente. He realizado una adaptación que no alertará si un paso ha fallado, pero luego fue un éxito al intentar nuevamente.

SELECT step_id, MIN(run_status) 
FROM msdb.dbo.sysjobhistory 
WHERE instance_id > COALESCE((SELECT MAX(instance_id) FROM msdb.dbo.sysjobhistory 
           WHERE job_id = $(ESCAPE_SQUOTE(JOBID)) AND step_id = 0), 0) 
     AND job_id = $(ESCAPE_SQUOTE(JOBID)) 
GROUP BY step_id 
HAVING MIN(run_status) <> 1 -- success 

IF @@ROWCOUNT <> 0 
RAISERROR('FailedStep', 16, 1) 
3

@wqw 's aceptado La respuesta es excelente.

Lo he extendido para aquellos que tienen habilitado el Correo de la base de datos para enviar un poco más de detalle sobre exactamente qué falló y cómo. También incorpora la respuesta de icvader en esta página para tener en cuenta los reintentos.

Debería ser realmente útil para aquellos de nosotros que necesitamos más detalles para juzgar si se requiere una acción urgente cuando fuera de servicio/de guardia.

DECLARE 

@YourRecipients as varchar(1000) = '[email protected]' 
,@YourMailProfileName as varchar(255) = 'Database Mail' 

,@Msg as varchar(1000) 
,@NumofFails as smallint 
,@JobName as varchar(1000) 
,@Subj as varchar(1000) 
,@i as smallint = 1 


---------------Fetch List of Step Errors------------ 
SELECT * 
INTO #Errs 

FROM 

    (
    SELECT 
     rank() over (PARTITION BY step_id ORDER BY step_id) rn 
    , ROW_NUMBER() over (partition by step_id order by run_date desc, run_time desc) ReverseTryOrder 
    ,j.name job_name 
    ,run_status 
    , step_id 
    , step_name 
    , [message] 

    FROM msdb.dbo.sysjobhistory h 
    join msdb.dbo.sysjobs j on j.job_id = h.job_id 

    WHERE instance_id > COALESCE((SELECT MAX(instance_id) FROM msdb.dbo.sysjobhistory 
            WHERE job_id = $(ESCAPE_SQUOTE(JOBID)) AND step_id = 0), 0) 
      AND h.job_id = $(ESCAPE_SQUOTE(JOBID)) 
    ) as agg 

WHERE ReverseTryOrder = 1 ---Pick the last retry attempt of each step 
    AND run_status <> 1 -- show only those that didn't succeed 


SET @NumofFails = ISNULL(@@ROWCOUNT,0)---Stored here because we'll still need the rowcount after it's reset. 


-------------------------If there are any failures assemble email and send ------------------------------------------------ 
IF @NumofFails <> 0 
    BEGIN 

     DECLARE @PluralS as char(1) = CASE WHEN @NumofFails > 1 THEN 's' ELSE '' END ---To make it look like a computer knows English 
     SELECT top 1 @Subj = 'Job: ' + job_name + ' had ' + CAST(@NumofFails as varchar(3)) + ' step' + @PluralS + ' that failed' 
        ,@Msg = 'The trouble is... ' +CHAR(13) + CHAR(10)+CHAR(13) + CHAR(10) 

         FROM dbo.#Errs 


     WHILE @i <= @NumofFails 
     BEGIN 
      SELECT @Msg = @Msg + 'Step:' + CAST(step_id as varchar(3)) + ': ' + step_name +CHAR(13) + CHAR(10) 

      + [message] +CHAR(13) + CHAR(10)+CHAR(13) + CHAR(10) FROM dbo.#Errs 
      WHERE rn = @i 


      SET @i = @i + 1 
     END 

      exec msdb.dbo.sp_send_dbmail 
      @recipients = @YourRecipients, 
      @subject = @Subj, 
      @profile_name = @YourMailProfileName, 
      @body = @Msg 


    END 

Una diferencia con respecto a las otras respuestas en las que se basa: No plantea todo el trabajo como un error. Eso es para mantener la distinción en el historial de trabajos entre Cancelado y Completado con errores.

1

La respuesta de Adamantish es la solución perfecta (gracias): funcionó sin problemas ... ediciones menores. Como se indicó anteriormente, no funcionará en SSMS, agregar esto como último paso y ejecutar el trabajo.

WHERE instance_id > COALESCE 
(
(
SELECT MAX(instance_id) 
FROM msdb.dbo.sysjobhistory 
WHERE job_id = '2XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXX' AND step_id = 0), 0 
) 
AND h.job_id = '2XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXX' 
) 
as agg 
1

Una mejora de la respuesta anterior, en caso de que alguien quiere utilizar los operadores en el Agente SQL Server para enviar correo electrónico; y utilice el nombre de perfil de base de datos almacenado en msdb:

DECLARE @EmailRecipients as varchar(1000) 
DECLARE @MailProfileName as varchar(255) 
DECLARE @Msg as varchar(1000) 
DECLARE @NumofFails as smallint 
DECLARE @JobName as varchar(1000) 
DECLARE @Subj as varchar(1000) 
DECLARE @i as smallint = 1 

SELECT @EmailRecipients = email_address 
FROM msdb.dbo.sysoperators 
WHERE name = <Operator Name> 

SELECT TOP(1) @MailProfileName = name 
FROM msdb.dbo.sysmail_profile 

SELECT * INTO #Errs 
FROM 
    (SELECT rank() over (PARTITION BY step_id ORDER BY step_id) rn, 
      ROW_NUMBER() over (partition by step_id order by run_date desc, run_time desc) ReverseTryOrder, 
      j.name job_name, 
      run_status, 
      step_id, 
      step_name, 
      [message] 
    FROM msdb.dbo.sysjobhistory h 
    JOIN msdb.dbo.sysjobs j ON j.job_id = h.job_id 
    WHERE instance_id > COALESCE((SELECT MAX(instance_id) FROM msdb.dbo.sysjobhistory 
            WHERE job_id = $(ESCAPE_SQUOTE(JOBID)) AND step_id = 0), 0) 
    AND h.job_id = $(ESCAPE_SQUOTE(JOBID)) 
    ) AS agg 
WHERE ReverseTryOrder = 1 ---Pick the last retry attempt of each step 
AND run_status <> 1 -- show only those that didn't succeed 


SET @NumofFails = ISNULL(@@ROWCOUNT,0)---Stored here because we'll still need the rowcount after it's reset. 

IF @NumofFails <> 0 
BEGIN 
    DECLARE @PluralS as char(1) = CASE WHEN @NumofFails > 1 THEN 's' ELSE '' END 

    SELECT top 1 @Subj = job_name + ':'+ CAST(@NumofFails as varchar(3)) + '''Check Steps'' Report', 
       @Msg = '''Check Steps'' has reported that one or more Steps failed during execution of ' + job_name + CHAR(13) + CHAR(10)+ CHAR(13) + CHAR(10) 
    FROM dbo.#Errs 

    WHILE @i <= @NumofFails 
    BEGIN 
     SELECT @Msg = @Msg + 'Step ' + CAST(step_id as varchar(3)) + ': ' + step_name +CHAR(13) + CHAR(10) 
        + [message] +CHAR(13) + CHAR(10)+CHAR(13) + CHAR(10) 
     FROM dbo.#Errs 
     WHERE rn = @i 

     SET @i = @i + 1 
    END 

    EXEC msdb.dbo.sp_send_dbmail 
    @recipients = @EmailRecipients, 
    @subject = @Subj, 
    @profile_name = @MailProfileName, 
    @body = @Msg 
END 
+0

No he mirado esto de cerca, pero diré una cosa. Es una maldita mejor vista que la forma en que solía escribir SQL en aquel entonces. – Adamantish

Cuestiones relacionadas