获取SQL Server作业失败的电子邮件

时间:2016-06-07 20:48:08

标签: sql-server tsql sql-server-2008-r2

我试图按照链接发送自动电子邮件,其中包含作业失败的描述。

但是,当我试图将这些参数传递给过程时:

EXEC msdb.dbo.TestMail
 @job_id = '$(ESCAPE_SQUOTE(JOBID))'
,@strtdt = '$(ESCAPE_SQUOTE(STRTDT))'
,@strttm = '$(ESCAPE_SQUOTE(STRTTM))'
,@operator_name = 'MSXOperator'
,@mail_on_success = 'Y'
,@attach_output_file = 'ON_FAILUR

我看到以下错误:

Msg 8114, Level 16, State 5, Procedure TestMail, Line 0
Error converting data type varchar to uniqueidentifier.

TestMail的代码是

USE msdb
GO

IF OBJECT_ID('dbo.TestMail') IS NOT NULL DROP PROC dbo.TestMail 
GO

CREATE PROC dbo.TestMail
@job_id uniqueidentifier
,@strtdt varchar(100)
,@strttm varchar(100)
,@operator_name sysname = 'MSXOperator'
,@mail_on_success char(1) = 'Y' --'Y', 'N'
,@attach_output_file varchar(10) = 'ON_FAILURE' --'ALWAYS', 'NEVER', 'ON_FAILURE'
AS


SET NOCOUNT ON

DECLARE
@job_name sysname
,@job_id_str varchar(200) --GUID representation as string without hyphens
,@email_address nvarchar(300)
,@run_status int --0 = Failed, 1 = Succeeded, 2 = Retry, 3 = Canceled
,@run_status_desc varchar(9) --Failed, Succeeded, Retry, Canceled
,@importance varchar(6) --low, normal, high
,@output_file_names varchar(max)
,@subject nvarchar(255)
,@body nvarchar(max)
,@step_name sysname --to hold name of jobstep
,@step_duration int
,@step_duration_str varchar(20)
,@job_duration int
,@job_duration_str varchar(20)
,@step_id int
,@step_run_status int
,@step_run_status_desc varchar(9)
,@crlf char(2)
,@send_mail_bit bit --calculated just before send mail routine
,@attach_output_file_bit bit --calculated just before send mail routine
,@ag_tkn_step_id varbinary(200)
,@ag_tkn_job_id varbinary(200)
,@ag_tkn_strt_dt varbinary(200)
,@ag_tkn_strt_tm varbinary(200)
,@ag_tkn_mach_nm varbinary(200)
,@ag_tkn_inst_nm varbinary(200)

SET @crlf = CHAR(13) + CHAR(10)
SET @body = ''

------------------------------------------------------------------------------------------------------------------------------
--We can't represent agent tokens as strings if we want to push this proc out from an MSX server. 
--The first SELECT is only used in dev, to ger each strin representation as varbinary.
--  This will look weird when deployed on a TSX server.
--The second part sets each variable to a varbinary representation of each string.
--The variable are used later in the proc
------------------------------------------------------------------------------------------------------------------------------
--SELECT
-- CAST('$(ESCAPE_SQUOTE(STEPID))' AS varbinary(200))
--,CAST('$(ESCAPE_SQUOTE(JOBID))'  AS varbinary(200))
--,CAST('$(ESCAPE_SQUOTE(STRTDT))' AS varbinary(200))
--,CAST('$(ESCAPE_SQUOTE(STRTTM))' AS varbinary(200))
--,CAST('$(ESCAPE_SQUOTE(MACH))'   AS varbinary(200))
--,CAST('$(ESCAPE_SQUOTE(INST))'   AS varbinary(200))

SET @ag_tkn_step_id = 0x24284553434150455F5351554F5445285354455049442929
SET @ag_tkn_job_id =  0x24284553434150455F5351554F5445284A4F4249442929
SET @ag_tkn_strt_dt = 0x24284553434150455F5351554F5445285354525444542929
SET @ag_tkn_strt_tm = 0x24284553434150455F5351554F54452853545254544D2929
SET @ag_tkn_mach_nm = 0x24284553434150455F5351554F5445284D4143482929
SET @ag_tkn_inst_nm = 0x24284553434150455F5351554F544528494E53542929


------------------------------------------------------------------------------------------------------------------------------
--Validate input parameters
-------------------------------------------------------------------------------------------------------------------------------
IF @mail_on_success NOT IN('Y', 'N')
BEGIN
   RAISERROR('Bad value for parameter @mail_on_success, values allowed are ''Y'' and ''N''.', 16, 1)
   RETURN
END

IF @attach_output_file NOT IN ('ALWAYS', 'NEVER', 'ON_FAILURE')
BEGIN
   RAISERROR('Bad value for parameter @attach_output_file, values allowed are ''ALWAYS'', ''NEVER'' andd ''ON_FAILURE''.', 16, 1)
   RETURN
END

-------------------------------------------------------------------------------------------------------------------------------
--Get job name
------------------------------------------------------------------------------------------------------------------------------
SET @job_name = (SELECT s.name FROM msdb.dbo.sysjobs AS s WHERE s.job_id = @job_id)
IF @job_name IS NULL
BEGIN
   RAISERROR('Failed to retreive job name baed on @job_id, teminating procedure MailAfterJob.', 16, 1)
   RETURN
END

------------------------------------------------------------------------------------------------------------------------------
--String representation of job_id (to match representation in file name)
------------------------------------------------------------------------------------------------------------------------------
SET @job_id_str = UPPER(master.dbo.fn_varbintohexstr(@job_id))

------------------------------------------------------------------------------------------------------------------------------
--Get email_address for operator
-------------------------------------------------------------------------------------------------------------------------------
SET @email_address = (SELECT o.email_address FROM msdb.dbo.sysoperators AS o WHERE o.name = @operator_name)
IF @email_address IS NULL
BEGIN
   RAISERROR('Unknown mail operator name, teminating procedure MailAfterJob.', 16, 1)
   RETURN
END

------------------------------------------------------------------------------------------------------------------------------
--Get job outcome for *this* execuution, store in table variable
------------------------------------------------------------------------------------------------------------------------------
DECLARE @jobhistory table(instance_id int, step_id int, run_status int, step_name sysname, step_duration int)
INSERT INTO @jobhistory (instance_id, step_id, run_status, step_name, step_duration)
  SELECT instance_id, step_id, run_status, step_name, run_duration
  FROM msdb.dbo.sysjobhistory AS h 
  WHERE h.job_id = @job_id
  AND msdb.dbo.agent_datetime(h.run_date, h.run_time) >= msdb.dbo.agent_datetime(CAST(@strtdt AS int), CAST(@strttm AS int))

------------------------------------------------------------------------------------------------------------------------------
--Get lowest run status for this execution (0 = fail)
------------------------------------------------------------------------------------------------------------------------------
SET @run_status = (SELECT MIN(h.run_status)
                   FROM @jobhistory AS h
                   INNER JOIN msdb.dbo.sysjobhistory AS hi ON hi.instance_id = h.instance_id
                   WHERE hi.job_id = @job_id)

IF @run_status IS NULL
BEGIN
   RAISERROR('Could not determine run status for job, teminating procedure MailAfterJob.', 16, 1)
   RETURN
END
SET @run_status_desc = CASE @run_status 
                         WHEN 0 THEN 'FAILED' 
                         WHEN 1 THEN 'SUCCEEDED' 
                         WHEN 2 THEN 'RETRY' 
                         WHEN 3 THEN 'CANCELED' 
                       END

------------------------------------------------------------------------------------------------------------------------------
--Set importance for email
------------------------------------------------------------------------------------------------------------------------------
IF @run_status = 0
SET @importance = 'high'
ELSE
SET @importance = 'low'

------------------------------------------------------------------------------------------------------------------------------
--Get output file names to attach to email, in table variable
------------------------------------------------------------------------------------------------------------------------------
DECLARE @output_file_names_table table(output_file_name_step varchar(300))

INSERT INTO @output_file_names_table(output_file_name_step)
  SELECT REPLACE(COALESCE(s.output_file_name, ''), CAST(@ag_tkn_step_id AS varchar(200)), CAST(s.step_id AS varchar(20))) AS out_file_name 
  FROM msdb.dbo.sysjobsteps AS s
  WHERE s.job_id = @job_id
  AND s.output_file_name IS NOT NULL
  AND EXISTS(SELECT * FROM @jobhistory AS h WHERE h.step_id = s.step_id)

--Replace agent tokens with actual values
UPDATE @output_file_names_table SET output_file_name_step = REPLACE(output_file_name_step, CAST(@ag_tkn_job_id AS varchar(200)), @job_id_str)
UPDATE @output_file_names_table SET output_file_name_step = REPLACE(output_file_name_step, CAST(@ag_tkn_strt_dt AS varchar(200)), @strtdt)
UPDATE @output_file_names_table SET output_file_name_step = REPLACE(output_file_name_step, CAST(@ag_tkn_strt_tm AS varchar(200)), @strttm)
UPDATE @output_file_names_table SET output_file_name_step = REPLACE(output_file_name_step, CAST(@ag_tkn_mach_nm AS varchar(200)), CAST(SERVERPROPERTY('MachineName') AS varchar(100)))
UPDATE @output_file_names_table SET output_file_name_step = REPLACE(output_file_name_step, CAST(@ag_tkn_inst_nm AS varchar(200)), (ISNULL(CAST(SERVERPROPERTY('InstanceName') AS varchar(100)), '')))

--Loop table with file names, create semi-colon separated string
DECLARE @output_file_name_step varchar(300)
SET @output_file_names = ''
DECLARE c CURSOR FOR SELECT DISTINCT output_file_name_step FROM @output_file_names_table
OPEN c
WHILE 1 = 1
BEGIN
  FETCH NEXT FROM c INTO @output_file_name_step
  IF @@FETCH_STATUS <> 0
    BREAK
  SET @output_file_names = @output_file_names + @output_file_name_step + ';'

END
CLOSE c
DEALLOCATE c
--Remove the last semi-colon
IF LEN(@output_file_names) > 0
  SET @output_file_names = SUBSTRING(@output_file_names, 1, LEN(@output_file_names) - 1)

------------------------------------------------------------------------------------------------------------------------------
--Construct email parts
------------------------------------------------------------------------------------------------------------------------------
--Set mail subject
--SET @subject = @@SERVERNAME + ' ' + @run_status_desc + ' ' + @job_name
SET @subject = CASE WHEN @run_status_desc = 'SUCCEEDED' THEN 'Ok' ELSE @run_status_desc END + ': ' + @job_name

--Set mail body
DECLARE c cursor FOR SELECT h.step_id, h.step_name, h.step_duration, h.run_status FROM @jobhistory AS h ORDER BY instance_id
OPEN c
WHILE 1 = 1
BEGIN
  FETCH NEXT FROM c INTO @step_id, @step_name, @step_duration, @step_run_status
  IF @@FETCH_STATUS <> 0
    BREAK
  SET @step_duration_str = RIGHT('00000' + CAST(@step_duration AS varchar(6)), 6) --Make sure we have 0:s first
  SET @step_duration_str = SUBSTRING(@step_duration_str, 1, 2) + ':' + 
                           SUBSTRING(@step_duration_str, 3, 2) + ':' + SUBSTRING(@step_duration_str, 5, 2) 
  SET @step_run_status_desc = CASE @step_run_status 
                                 WHEN 0 THEN 'FAILED' 
                                 WHEN 1 THEN 'SUCCEEDED' 
                                 WHEN 2 THEN 'RETRY' 
                                 WHEN 3 THEN 'CANCELED' 
                                END
  IF @step_id <> 0
    SET @body = @body + 'Step "' + @step_name + '" executed ' + @step_run_status_desc + ', time ' + @step_duration_str + '.' + @crlf
END
CLOSE c
DEALLOCATE c
SET @job_duration = (SELECT SUM(step_duration) FROM @jobhistory)
SET @job_duration_str = RIGHT('00000' + CAST(@job_duration AS varchar(6)), 6) --Make sure we have 0:s first
SET @job_duration_str = SUBSTRING(@job_duration_str, 1, 2) + ':' + 
                        SUBSTRING(@job_duration_str, 3, 2) + ':' + SUBSTRING(@job_duration_str, 5, 2) 
SET @body = 'Job executed, time ' + @job_duration_str + '.' + @crlf + @crlf + @body

------------------------------------------------------------------------------------------------------------------------------
--Decide whether to send email
------------------------------------------------------------------------------------------------------------------------------
IF (@mail_on_success = 'N' AND @run_status = 1) --1 = Success
  SET @send_mail_bit = 0
ELSE
  SET @send_mail_bit = 1

------------------------------------------------------------------------------------------------------------------------------
--Decide whether to attach output file
------------------------------------------------------------------------------------------------------------------------------
SET @attach_output_file_bit = 0
IF @attach_output_file = 'ALWAYS'
  SET @attach_output_file_bit = 1
IF @attach_output_file = 'NEVER'
  SET @attach_output_file_bit = 0
IF @attach_output_file = 'ON_FAILURE' AND @run_status <> 1 --1 = Success
  SET @attach_output_file_bit = 1

------------------------------------------------------------------------------------------------------------------------------
--Send the email
------------------------------------------------------------------------------------------------------------------------------
IF @send_mail_bit = 1
BEGIN
  IF @attach_output_file_bit = 0
      EXEC msdb.dbo.sp_send_dbmail    --Do no attach output file
        @recipients = @email_address
       ,@subject = @subject
       ,@body = @body
       ,@importance = @importance
  ELSE
      EXEC msdb.dbo.sp_send_dbmail    --Do attach output file
        @recipients = @email_address
       ,@subject = @subject
       ,@body = @body
       ,@importance = @importance
       ,@file_attachments = @output_file_names
END

------------------------------------------------------------------------------------------------------------------------------
--Exit with fail if we got here on failure
------------------------------------------------------------------------------------------------------------------------------
IF @run_status = 0
  RAISERROR('Job failed', 16, 1)

------------------------------------------------------------------------------------------------------------------------------
/*
--Sample execution, as to be defined in job (this can look weird when deployed on TSX server)
EXEC sqlmaint.dbo.MailAfterJob 
@job_id = $(ESCAPE_SQUOTE(JOBID))
,@strtdt = '$(ESCAPE_SQUOTE(STRTDT))'
,@strttm = '$(ESCAPE_SQUOTE(STRTTM))'
,@operator_name = 'MSXOperator'
,@mail_on_success = 'Y'
,@attach_output_file = 'ON_FAILURE' --'ALWAYS', 'NEVER', 'ON_FAILURE'
*/
------------------------------------------------------------------------------------------------------------------------------
GO 

链接是:http://www.karaszi.com/sqlserver/util_MailAfterJob.asp

1 个答案:

答案 0 :(得分:0)

我能够复制错误;它准确地表明了问题。问题是您引用了令牌VARCHAR,导致proc认为您正在传递一个字符串,隐式转换为VARCHAR。由于UNIQUEIDENTIFIER未隐式转换为定义的use msdb go if OBJECT_ID('testString','P') is null execute sp_executesql N'CREATE PROC testString as select ''stub'' stubProc' go alter proc testString @job_id uniqueidentifier as declare @job_id_str varchar(200) SET @job_id_str = UPPER( master.dbo.fn_varbintohexstr( @job_id ) ) select @job_id jobID, @job_id_str jobIDStr ,因此您会收到错误。

如果您只是删除引号<作业步骤中的 ,则一切都按设计工作。希望您不要尝试在SQL代理作业之外进行测试,否则无论您做什么都会失败。

Simplified Proc来解决转换错误:

USE [msdb]
GO

/****** Object:  Job [id TEST]    Script Date: 6/8/2016 9:06:06 AM ******/
EXEC msdb.dbo.sp_delete_job @job_name=N'id TEST', @delete_unused_schedule=1
GO

/****** Object:  Job [id TEST]    Script Date: 6/8/2016 9:06:06 AM ******/
BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
/****** Object:  JobCategory [[Uncategorized (Local)]]    Script Date: 6/8/2016 9:06:06 AM ******/
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'id TEST', 
        @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'GEHA\F073', @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
/****** Object:  Step [TEST]    Script Date: 6/8/2016 9:06:06 AM ******/
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'TEST', 
        @step_id=1, 
        @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'EXEC msdb.dbo.testString
 @job_id = $(ESCAPE_SQUOTE(JOBID))', 
        @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:

测试作业

  "registry": {
      "search": [
                   "http://bower.herokuapp.com",
                   "http://localhost:65200/"
      ]
  }

GO