2016-06-07 2 views
0

Я пытаюсь перейти по ссылке, чтобы отправлять автоматические письма с описаниями неудач задания.Получение электронной почты для сбоев в работе SQL Server

Однако, когда я пытаюсь передать эти аргументы в процедуру:

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

+0

Ссылка показывает @job_id как '$ (ESCAPE_SQUOTE (JobId)) 'без кавычек. Похоже, у тебя немного сумасшедшая цитата. –

+0

@SteveMangiameli Yeah) Я добавил цитаты, а затем он дал мне вышеуказанную ошибку :) – Jack

+1

Можете ли вы разместить код для 'TestMail'? У вас может быть несоответствие типа данных для @job_id –

ответ

0

Я смог повторить ошибку; это точно указывает на проблему. Проблема в том, что вы цитируете токен '$(ESCAPE_SQUOTE(JOBID))', заставляя proc считать, что вы передаете строку, неявно преобразованную в VARCHAR. Поскольку VARCHAR неявно не конвертируется в заданный UNIQUEIDENTIFIER, вы получаете свою ошибку.

Если вы просто удалить кавычки, на этапе работы, все работает, как задумано. Надеюсь, вы не пытаетесь протестировать это за пределами работы агента SQL, иначе он будет терпеть неудачу независимо от того, что вы делаете.

Упрощенная Proc в адрес ошибки преобразования:

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 

тестовое задание

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 @[email protected], @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: 

GO

+0

Большое вам спасибо за помощь, Стив! Я просто новичок, поэтому немного запутался в том, что делать с кодом, который вы написали. И если я удаляю кавычки из исходного кода, он говорит «Неверный синтаксис рядом с« ESCAPE_SQUOTE » – Jack

+0

Вы пытаетесь проверить свой код как сценарий в SSMS или как шаг в задании? Код, выполняющий SP, предназначен для запуска в задании агента SQL.Если вы запустите его как обычный SQL-скрипт в SSMS, он не сработает с описанной вами ошибкой. Выполните код выше в среде разработки, и вы увидите, что происходит. –

+0

Я пытался проверить код как скрипт.Так что я должен вызывать процедуру и передавать аргументы (без кавычек) в качестве шага в задании? – Jack

Смежные вопросы