Getting the emails for SQL Server Job Failures - sql-server

I'm trying to follow the link in order to send automated emails with the descriptions of the job failures.
However, when I'm trying to pass these arguments into the procedure:
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
I see the following error:
Msg 8114, Level 16, State 5, Procedure TestMail, Line 0
Error converting data type varchar to uniqueidentifier.
The code for TestMail is
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
The link is: http://www.karaszi.com/sqlserver/util_MailAfterJob.asp

I was able to duplicate the error; it indicates the problem exactly. The problem is that you are quoting the token '$(ESCAPE_SQUOTE(JOBID))' causing the proc to think you are passing a string, implicitly converted to VARCHAR. Since VARCHAR doesn't implicitly convert to the defined UNIQUEIDENTIFIER, you get your error.
If you simply remove the quotes, in the job step, everything works as designed. Hopefully, you aren't trying to test this outside of a SQL Agent job or it will fail no matter what you do.
Simplified Proc to Address the conversion error:
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
Test job
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'DOMAIN\USER', #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:
GO

Related

SQL Find out which login/user has modified a table structure

I need to find all DDL operations executed and the user associated about a table in a SQL Server database.
How can I do that?
PS:
The operation has been already executed.
You can achieve it by WMI Events - SQL Alert
Following script would create alert in SQL Agent section:
Step1 - WMI Event Alert
These part of the code to be replaced as per your environment:
\\.\root\Microsoft\SqlServer\ServerEvents\SQLInstanceName at the end enter the SQL Instance name. if your server is default instance use MSSQLSERVER
USE [msdb]
GO
EXEC msdb.dbo.sp_add_alert #name=N'WMI - DDL_TABLE_EVENTS',
#message_id=0,
#severity=0,
#enabled=1,
#delay_between_responses=0,
#include_event_description_in=1,
#category_name=N'[Uncategorized]',
#wmi_namespace=N'\\.\root\Microsoft\SqlServer\ServerEvents\SQLInstanceName',
#wmi_query=N'select * from DDL_TABLE_EVENTS WHERE (DatabaseName ISA "MyDB")', --- This alert on one database, you can remove "WHERE" caluse to focus on all tables
#job_id=N'819e9aea-95dd-49d3-b518-49060b1910a4'
GO
Step 2 - SQL Job
Script to create SQL Job that triggered by Alert (Created step 1). it would also load the same event information into local audit table that desire to have.
These part of the code to be replaced as per your environment:
DatabaseName.DBO.YourAuditLogTable
#profile_name = ''ProfileName''
#recipients = ''youremail#domain.AE''
USE [msdb]
GO
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'WMI Response - DDL_TABLE_EVENTS',
#enabled=1,
#notify_level_eventlog=0,
#notify_level_email=0,
#notify_level_netsend=0,
#notify_level_page=0,
#delete_level=0,
#description=N'Sends email to DBA on DDL_TABLE Event',
#category_name=N'[Uncategorized (Local)]',
#owner_login_name=N'DBA', #job_id = #jobId OUTPUT
IF (##ERROR <> 0 OR #ReturnCode <> 0) GOTO QuitWithRollback
/****** Object: Step [Send e-mail in response to WMI alert(s)] Script Date: 8/19/2019 12:52:29 PM ******/
EXEC #ReturnCode = msdb.dbo.sp_add_jobstep #job_id=#jobId, #step_name=N'Send e-mail in response to WMI alert(s)',
#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'DECLARE #p_subject NVARCHAR(255), #Body nvarchar (2000);
DECLARE #TargetServer nvarchar (25),
#TargetInstance nvarchar (25),
#TargetDatabase nvarchar (25),
#SourceHost nvarchar (25),
#EventType nvarchar (25),
#SourceLogin nvarchar (25);
Select #TargetServer = ''$(ESCAPE_SQUOTE(WMI(ComputerName)))''
select #TargetInstance = ''$(ESCAPE_SQUOTE(WMI(SQLInstance)))''
select #TargetDatabase = ''$(ESCAPE_SQUOTE(WMI(DatabaseName)))''
select #SourceLogin = ''$(ESCAPE_SQUOTE(WMI(LoginName)))'';
select #EventType = ''$(ESCAPE_SQUOTE(WMI(__CLASS)))'';
SELECT #p_subject = N''WMI Alert: '' + #EventType +
'' [$(ESCAPE_SQUOTE(WMI(DatabaseName)))]:[$(ESCAPE_SQUOTE(WMI(ComputerName)))\$(ESCAPE_SQUOTE(WMI(SQLInstance)))].'' ;
select #Body = N''WMI Alert DDL TABLE EVENTS: Computer Name: $(ESCAPE_SQUOTE(WMI(ComputerName)));
SQL Instance: $(ESCAPE_SQUOTE(WMI(SQLInstance)));
Database: $(ESCAPE_SQUOTE(WMI(DatabaseName)));
Login Name: $(ESCAPE_SQUOTE(WMI(LoginName)));
PostTime: $(ESCAPE_SQUOTE(WMI(PostTime)));
EventType: $(ESCAPE_SQUOTE(WMI(__CLASS)));
Command: $(ESCAPE_SQUOTE(WMI(TSQLCommand)));
'';
insert into DatabaseName.DBO.YourAuditLogTable
([EventCreatedOn] ,[EventType] ,[TargetServerName] ,[TargetInstanceName],[TargetDatabaseName], [SourceLogin] ,[AuditData])
values
(GETDATE(), #EventType, #TargetServer, #TargetInstance, #TargetDatabase, #SourceLogin, #Body);
EXEC msdb.dbo.sp_send_dbmail
#profile_name = ''ProfileName'', -- update with your values
#recipients = ''youremail#domain.AE'', -- update with your values
#subject = #p_subject,
#body = #Body
',
#database_name=N'msdb',
#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
i've found this one but it retrieves only the last operations
begin try
declare #enable int
select top 1 #enable = convert(int,value_in_use) from sys.configurations where name = 'default trace enabled'
if #enable = 1 --default trace is enabled
begin
declare #d1 datetime;
declare #diff int;
declare #curr_tracefilename varchar(500);
declare #base_tracefilename varchar(500);
declare #indx int ;
declare #temp_trace table (
obj_name nvarchar(256) collate database_default
, database_name nvarchar(256) collate database_default
, start_time datetime
, event_class int
, event_subclass int
, object_type int
, server_name nvarchar(256) collate database_default
, login_name nvarchar(256) collate database_default
, application_name nvarchar(256) collate database_default
, ddl_operation nvarchar(40) collate database_default
);
select #curr_tracefilename = path from sys.traces where is_default = 1 ;
set #curr_tracefilename = reverse(#curr_tracefilename)
select #indx = PATINDEX('%\%', #curr_tracefilename)
set #curr_tracefilename = reverse(#curr_tracefilename)
set #base_tracefilename = LEFT( #curr_tracefilename,len(#curr_tracefilename) - #indx) + '\log.trc';
insert into #temp_trace
select ObjectName
, DatabaseName
, StartTime
, EventClass
, EventSubClass
, ObjectType
, ServerName
, LoginName
, ApplicationName
, 'temp'
from ::fn_trace_gettable( #base_tracefilename, default )
where EventClass in (46,47,164) and EventSubclass = 0 and DatabaseID <> 2
update #temp_trace set ddl_operation = 'CREATE'where event_class = 46
update #temp_trace set ddl_operation = 'DROP' where event_class = 47
update #temp_trace set ddl_operation = 'ALTER' where event_class = 164
select #d1 = min(start_time) from #temp_trace
set #diff= datediff(hh,#d1,getdate())
set #diff=#diff/24;
select #diff as difference
, #d1 as date
, object_type as obj_type_desc
, *
from #temp_trace where object_type not in (21587)
order by start_time desc
end
else
begin
select top 0 1 as difference, 1 as date, 1 as obj_type_desc, 1 as obj_name, 1 as dadabase_name, 1 as start_time, 1 as event_class, 1 as event_subclass, 1 as object_type, 1 as server_name, 1 as login_name, 1 as application_name, 1 as ddl_operation
end
end try
begin catch
select -100 as difference
, ERROR_NUMBER() as date
, ERROR_SEVERITY() as obj_type_desc
, ERROR_STATE() as obj_name
, ERROR_MESSAGE() as database_name
, 1 as start_time, 1 as event_class, 1 as event_subclass, 1 as object_type, 1 as server_name, 1 as login_name, 1 as application_name, 1 as ddl_operation
end catch

Looking for help exiting out of loop

I have been working to replicate the Box feature of AutoSys. I came across a solution shown here (https://dba.stackexchange.com/a/161658), that works very, very well. I started to add to it, checking outcome, and added handling of another ETL solution we use.
Where, I ran into trouble, is when I realized nothing was checking if a job was valid. I want to check this in case a job name is spelled incorrectly, or if someone removes a job. I don't want to assume a job is being executed if it isn't. I added a check for valid SQL Agent Job name. This works, if the job names are valid. However, if the job name is not valid, the procedure gets stuck in a loop displaying the error message 'NO JOB' until I stop the procedure.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[usp_start_job_sequence8]
(
#JobList JobSequenceTable READONLY
,#PrntJob VARCHAR(100) = 'Unknown_Job'
)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
SET QUOTED_IDENTIFIER ON;
SET ANSI_NULLS ON;
SET ANSI_PADDING ON;
SET ARITHABORT ON;
SET CONCAT_NULL_YIELDS_NULL ON;
SET NUMERIC_ROUNDABORT OFF;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET ANSI_WARNINGS OFF;
---------------------************TRY BLOCK************---------------------
BEGIN TRY
BEGIN
DECLARE
#JobNumber TINYINT = 1
,#JobName VARCHAR(100)
,#IsRunning BIT
,#IsEnabled BIT
,#JOB_ID VARCHAR(60) = NULL
,#JOB_HIST_ID INT
,#JOB_STATUS VARCHAR(30)
,#JOB_STATUS_ID INT
,#esub VARCHAR(100)
,#ebdy VARCHAR(500)
,#Envt VARCHAR(4)
,#OVJOB_ID VARCHAR(60)
,#OVJOB_NAME VARCHAR(120)
,#JOB_TYPE CHAR(3)
,#epri VARCHAR(6);
--- Set server environment for emails
SELECT
#Envt = CASE WHEN ##SERVERNAME LIKE '%D%' THEN 'Dev'
WHEN ##SERVERNAME LIKE '%U%' THEN 'UAT'
WHEN ##SERVERNAME LIKE '%P%' THEN 'Prod'
WHEN ##SERVERNAME LIKE '%R%' THEN 'BCP'
ELSE ''
END
--- Set server environment for email priority
,#epri = CASE WHEN ##SERVERNAME LIKE '%D%' THEN 'Low'
WHEN ##SERVERNAME LIKE '%U%' THEN 'Normal'
WHEN ##SERVERNAME LIKE '%P%' THEN 'High'
WHEN ##SERVERNAME LIKE '%R%' THEN 'High'
ELSE ''
END;
BEGIN
WHILE (#JobNumber <= (SELECT
MAX(JobNumber)
FROM
#JobList
))
BEGIN
SELECT
#JobName = JobName
FROM
#JobList
WHERE
JobNumber = #JobNumber;
--VALID JOB?
IF NOT EXISTS(SELECT j.name FROM msdb.dbo.sysjobs_view J WITH(NOLOCK)
WHERE j.Name = #JobName)
BEGIN
PRINT 'NO JOB'
END;
ELSE
BEGIN
PRINT 'YES WE FOUND THE JOB';
--END
SELECT
#JOB_ID = job_id
FROM
msdb.dbo.sysjobs_view
WHERE
name = #JobName;
SELECT
#IsEnabled = enabled
FROM
msdb.dbo.sysjobs_view
WHERE
name = #JobName;
--- Very important step here. Ouvvi job names must start with Ouvvi
SELECT
#JOB_TYPE = CASE WHEN #JobName LIKE 'Ouvvi%'
THEN 'OVI'
ELSE 'SQL'
END;
--- Check if the job already running
SELECT
#IsRunning = dbo.fnJobStatusCheck(#JobName);
IF #IsRunning = 0
BEGIN
IF #IsEnabled = 0 --- Job is disabled error and send email
BEGIN
PRINT 'Job ' + #JobName
+ ' is disabled and cannot be started';
SET #esub = 'SQL Agent job '
+ #JobName + ' in ' + #Envt
+ ' is disabled and cannot be started';
SET #ebdy = 'SQL Agent job '
+ #JobName
+ ' was scheduled to run in box job '
+ #PrntJob + ' on server '
+ ##SERVERNAME + '. '
+ #JobName
+ ' could not start as it is disabled.'
+ CHAR(10) + CHAR(13)
+ +'The job ' + #JobName
+ ' should either be enabled, or removed from box job '
+ #PrntJob + '.';
EXEC msdb.dbo.sp_send_dbmail
--#profile_name = '',
-- #recipients = 'group#mail.com'
#recipients = 'person#mail.com',
#importance = #epri,
#subject = #esub,#body = #ebdy;
END;
ELSE ---- #IsEnabled = 1
----- Job is not running nor disabled. Split for different types
---OUVVI
BEGIN
IF #JOB_TYPE = 'OVI'
BEGIN
--PRINT 'OUVVI JOB'; --- TESTING
--- Parse Ouvvi Project ID - Used for success-failure
SET #OVJOB_ID = (SELECT
RTRIM(SUBSTRING(command,
CHARINDEX('/start/',
command) + 7,3))
FROM
msdb.dbo.sysjobsteps
WHERE
job_id = #JOB_ID
AND step_id = 1
);
--- START Ouvvi Job
EXEC msdb.dbo.sp_start_job #job_name = #JobName;
--PRINT #OVJOB_ID; --- TESTING
-- Waiting for the job to finish - Ouvvi jobs don't start immediately
WAITFOR DELAY '00:00:01';
WHILE (SELECT
1
FROM
Ouvvi.dbo.Queue
WHERE
ProjectID = #OVJOB_ID
) IS NOT NULL
BEGIN
WAITFOR DELAY '00:00:15';
IF (SELECT
1
FROM
Ouvvi.dbo.Queue
WHERE
ProjectID = #OVJOB_ID
) IS NULL
BREAK;
END;
--- Get Ouvvi Job Hist ID
SET #JOB_HIST_ID = (SELECT
Instance.ID
FROM
Ouvvi.dbo.Instance
WHERE
Instance.ProjectID = #OVJOB_ID
AND Instance.EndTime = (SELECT
MAX(EndTime)
FROM
Ouvvi.dbo.Instance
WHERE
Instance.ProjectID = #OVJOB_ID
)
);
--- Get Ouvvi Result
SET #JOB_STATUS_ID = (SELECT
ISNULL(I.Result,
9)
FROM
Ouvvi.dbo.Instance I
WHERE
I.ID = #JOB_HIST_ID
);
SET #JOB_STATUS = (SELECT
CASE
WHEN I.Result = 1
THEN 'Succeeded'
WHEN I.Result = 2
THEN 'Failed'
WHEN I.Result = 3
THEN 'Cancelled'
ELSE 'Unknown'
END
FROM
Ouvvi.dbo.Instance I
WHERE
I.ID = #JOB_HIST_ID
);
IF #JOB_STATUS_ID <> 1
BEGIN
PRINT #JobName
+ ' erred with the following status: '
+ #JOB_STATUS;
SET #esub = 'Ouvvi SQL Agent job '
+ #JobName
+ ' in ' + #Envt
+ ' erred with the following status: '
+ #JOB_STATUS;
SET #ebdy = 'An Ouvvi job, scheduled in SQL Agent '
+ #JobName
+ ' has erred with the following status: '
+ #JOB_STATUS
+ ' on server '
+ ##SERVERNAME
+ '.';
EXEC msdb.dbo.sp_send_dbmail
--#profile_name = '',
-- #recipients = 'group#mail.com'
#recipients = 'person#mail.com',
#importance = #epri,
#subject = #esub,
#body = #ebdy;
END;
END;
----Its a SQL Server Job
ELSE
BEGIN
EXEC msdb.dbo.sp_start_job #job_name = #JobName;
END;
WAITFOR DELAY '00:00:15.000';
SELECT
#IsRunning = dbo.fnJobStatusCheck(#JobName);
WHILE #IsRunning = 1
BEGIN
WAITFOR DELAY '00:00:15.000';
SELECT
#IsRunning = dbo.fnJobStatusCheck(#JobName);
END;
BEGIN
SET #JOB_HIST_ID = (SELECT
job_history_id
FROM
msdb.dbo.sysjobactivity
WHERE
job_id = #JOB_ID
AND run_requested_date = (SELECT
MAX(run_requested_date)
FROM
msdb.dbo.sysjobactivity
WHERE
job_id = #JOB_ID
)
);
SET #JOB_STATUS_ID = (SELECT
ISNULL(run_status,
9)
FROM
msdb.dbo.sysjobhistory
WHERE
instance_id = #JOB_HIST_ID
);
SET #JOB_STATUS = (SELECT
CASE
WHEN #JOB_STATUS_ID = 0
THEN 'Failed'
WHEN #JOB_STATUS_ID = 1
THEN 'Succeeded'
WHEN #JOB_STATUS_ID = 2
THEN 'Retry'
WHEN #JOB_STATUS_ID = 3
THEN 'Cancelled'
ELSE 'Unknown'
END
);
BEGIN
IF #JOB_STATUS_ID <> 1
BEGIN
PRINT #JobName
+ ' erred with the following status: '
+ #JOB_STATUS;
SET #esub = 'SQL Agent job '
+ #JobName
+ ' in ' + #Envt
+ ' erred with the following status: '
+ #JOB_STATUS;
SET #ebdy = 'SQL Agent job '
+ #JobName
+ ' erred with the following status: '
+ #JOB_STATUS
+ ' on server '
+ ##SERVERNAME
+ '.';
EXEC msdb.dbo.sp_send_dbmail
--#profile_name = '',
-- #recipients = 'group#mail.com'
#recipients = 'person#mail.com',
#importance = #epri,
#subject = #esub,
#body = #ebdy;
END;
END;
END;
END;
SET #JOB_ID = NULL;
SET #JobNumber = #JobNumber + 1;
END;
END;
END;
END;
END;
END TRY
---------------------*********************************--------------------
---------------------************CATCH BLOCK**********-------------------
BEGIN CATCH
-- Print Error Information
DECLARE #ERRORMESSAGE NVARCHAR(4000);
DECLARE #ERRORSEVERITY INT;
DECLARE #ERRORSTATE INT;
SELECT
#ERRORMESSAGE = ERROR_MESSAGE()
,#ERRORSEVERITY = ERROR_SEVERITY()
,#ERRORSTATE = ERROR_STATE();
RAISERROR (#ERRORMESSAGE, #ERRORSEVERITY, #ERRORSTATE);
-- Rollback uncommittable transactions
IF (XACT_STATE()) = -1
BEGIN
PRINT 'The transaction is in an uncommittable state.'
+ ' Rolling back transaction.';
ROLLBACK TRANSACTION;
END;
-- Inserting error related information into the Error Log table
INSERT INTO dbo.tbl_Object_ErrorLog
(ObjectName
,ErrorNumber
,ErrorMessage
,ErrorSeverity
,ErrorState
,ErrorlINE
,SystemUser
,LogDate
)
SELECT
ERROR_PROCEDURE()
,ERROR_NUMBER()
,ERROR_MESSAGE()
,ERROR_SEVERITY()
,ERROR_STATE()
,ERROR_LINE()
,SYSTEM_USER
,GETDATE();
END CATCH;
---------------------********************************----------------------
END;
GO
Code to launch the procedure:
SET ANSI_WARNINGS OFF
GO
DECLARE #JobList AS JobSequenceTable
INSERT INTO #JobList
VALUES
('x_test3')
,('NoJobHere')
,('x_test1')
EXEC dba.dbo.usp_start_job_sequence8 #JobList, 'TESTING'
What I want to happen is: when it checks for valid job name, prints NO JOB and ends, it should go to line 378, add 1 to #JobNumber, end that round, going to the next job.
I can't figure out why it's stuck in the loop.
Thanks for any help.
As your loop is very long with nested blocks and hard to debug, I would do the following:
- before jumping in the loop, validate which jobs are missing
- create a table that holds the missing jobs, e.g. #MissingJobs, and do your stuff with them before you do with the existing jobs
- based on #MissingJobs remove the missing jobs from the #JobList table and then loop through those, so in this case no need to debug the long loop block. Or create a new table if you need the original and use that in the loop - but in that case more work to change everywhere from #JobList to the new one. You can get back the original list if you UNION ALL the reduced #JobList with #MissingJobs. Anyway you do, you need to remove the block after --VALID JOB? (till PRINT 'YES WE FOUND THE JOB';) as you can PRINT those out based on this logic - in this case you must also remove an END after SET #JobNumber = #JobNumber + 1; because you remove an unclosed ELSE BEGIN.

MSSQL stored procedure call from ADO - not running properly

I have sp in MSSQL server - code below. When I run it from job, or SSMS it runs OK. But I need to run it from VB6 app with ADODB.
My VB6 code:
Dim cmd As New ADODB.Command
cmd.ActiveConnection = CNN
cmd.CommandTimeout = 180
cmd.CommandText = "dbbackup"
cmd.CommandType = ADODB.CommandTypeEnum.adCmdStoredProc
cmd.Execute(, , ADODB.ConnectOptionEnum.adAsyncConnect)
Problem is: When database backup is almost done - about 90+%, cmd.State changes from Executing to Closed and VB6 code continue in executing (to this moment it waits for sp to complete). But there is a lot of code after backup which never run this way(old backup delete,...). I realized that “Last database backup” property on MSSQL database was not set and in table msdb.dbo.backupset there are no rows for my backup. But there si good restorable backup on HDD.
When i stops program for 5 minutes in debug, sp runs properly to end and everything is OK. This backup code is last code in app run and after it ends program closes all connections and exits. I added wait to VB6 code and it helps on some servers, but many other servers still has same problem.
I think main question is why MSSQL server returns control flow to VB6 code and sp is not completed yet.
Thanks
sp code:
PROCEDURE [dbo].[dbBackup]
AS
BEGIN
SET NOCOUNT ON;
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE
If OBJECT_ID('tempdb..#DBName','u') IS NULL
Create Table #DBName
(
ID int identity (1,1) ,
Name varchar(128) not null ,
RetentionPeriod int null,
BackupPath varchar(255) default(''),
DBSize float default(0)
)
If OBJECT_ID('tempdb..#ExistingBackups', 'u') IS NULL
Create Table #ExistingBackups
(
Name varchar(128) ,
ID int identity (1,1)
)
Declare #Path varchar(255)
Declare #sql varchar(1000)
Declare #Name varchar(128)
Declare #RetentionPeriod int
Declare #LastBackupToKeep varchar(8)
Declare #ID int
Declare #MaxID int
Declare #eName varchar(255)
Declare #eMaxID int
Declare #eID int
Declare #eTimeStamp varchar(20)
Declare #errMsg nvarchar(2048)
Declare #errCount int; set #errCount = 0;
Declare #freeSpace bigint
Declare #pageSize float
Declare #dbSize bigint
Declare #procDate datetime
Declare #Sklad char(3)
Declare #backupName as varchar(255)
Select #pageSize = v.low / 1024 From master..spt_values v (noLock) Where v.number = 1 And v.[type] = 'E'
Select Top 1 #sklad = sklad_id From dbo.pohyb (noLock) Where Convert(int, sklad_id) > 500
Set #procDate = GETDATE()
Truncate Table #DBName
Insert Into #DBName (Name, RetentionPeriod, BackupPath)
Select DBName, BackupsToStore, BackupPath
From dbo.databaseBackup (noLock)
Where runBackup = 1
Select #MaxID = max(ID), #ID = 0 From #DBName
While #ID < #MaxID
Begin
Select #ID = min(ID) From #DBName Where ID > #ID
Select #Name = Name, #RetentionPeriod = RetentionPeriod, #Path = BackupPath
From #DBName
Where ID = #ID
If SUBSTRING(#Path, Len(#Path), 1) <> '\' Set #Path = #Path + '\'
Set #sql = 'Update #DBName Set DBSize= (Select Round(Sum(size) *' + CONVERT(varchar, #pageSize) + '/1024, 0) From ' + #Name + '.dbo.sysfiles (noLock)) Where Name = ''' + #Name + ''''
Exec (#sql)
Select #dbSize = DBSize From #DBName
--Exec #freeSpace = dbo.getDiskFreeSpace #drive = #Path
--If #freeSpace > #dbSize
--Begin
Set #eTimeStamp = REPLACE(REPLACE(CONVERT(varchar, #procDate, 113), ' ', '_'), ':', '-')
Set #sql = #Path + #Name + '_' + #eTimeStamp + '.bak'
Set #errMsg = 'OK'
Begin Try
SET #backupName = 'Objednavky backup by job ' + CONVERT(varchar, GETDATE(), 104) + ' ' + CONVERT(varchar, GETDATE(), 108);
Backup Database #Name To Disk = #sql
WITH NAME = #backupName;
-------mazanie backupu begin
Truncate Table #ExistingBackups
Set #sql = 'dir /B /OD ' + #Path + #Name + '_*.bak'
Insert #ExistingBackups Exec master..xp_cmdshell #sql
If Exists (Select 1 From #ExistingBackups Where PATINDEX('%File Not Found%', Name) > 0)
Truncate Table #ExistingBackups
Delete From #ExistingBackups Where Name IS NULL
Select #eID = 0
Select #eMaxID = Max(ID) - #RetentionPeriod From #ExistingBackups
While #eID < #eMaxID
Begin
Select #eID = Min(ID) From #ExistingBackups Where ID > #eID
Select #eName = Name From #ExistingBackups Where ID = #eID
Set #sql = 'del ' + #Path + #eName
Exec master..xp_cmdshell #sql
End
Truncate Table #ExistingBackups
-------mazanie backupu end
End Try
Begin Catch
Set #errMsg = #errMsg + '||' + CONVERT(varchar,ERROR_MESSAGE())
Set #errCount = #errCount + 1;
End Catch
--End
--Else
--Set #errMsg = 'Pln? disk (Vo?n? miesto: ' + CONVERT(varchar, #freeSpace) + ' MB, potrebn? aspo?: ' + CONVERT(varchar, #dbSize) + ' MB)'
Insert Into [dbo].[databaseBackup_log] ([Sklad_id], [DBName], [BackupDate], [Status]) Values (#Sklad, #Name, #procDate, Ltrim(Rtrim(CONVERT(varchar,#errMsg))))
End
Drop Table #DBName
Drop Table #ExistingBackups
IF #errCount > 0 BEGIN
RAISERROR (#errMsg, 16, 2) WITH SETERROR
END
RETURN 0;
END

Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT - Stored Procedure?

I am getting this exception about commits but am not sure what exactly is wrong with my Stored Procedure. I have read answers in other questions but am unable to find where exactly the commit count is getting messed up.
Herewith the Stored Procedure I use:
USE [AFS_GROUP]
GO
/****** Object: StoredProcedure [dbo].[SBO_SP_TransactionNotification] Script Date: 12/12/2012 10:41:57 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER proc [dbo].[SBO_SP_TransactionNotification]
#object_type nvarchar(20), -- SBO Object Type
#transaction_type nchar(1), -- [A]dd, [U]pdate, [D]elete, [C]ancel, C[L]ose
#num_of_cols_in_key int,
#list_of_key_cols_tab_del nvarchar(255),
#list_of_cols_val_tab_del nvarchar(255)
AS
begin
-- Return values
declare #error int -- Result (0 for no error)
declare #error_message nvarchar (200) -- Error string to be displayed
select #error = 0
select #error_message = N'Ok'
IF #object_type = 13 AND #transaction_type = 'A'
BEGIN
UPDATE INV1
SET U_jnl1='N', U_jnl2='N', U_jnl3='N', U_jnl4='N'
WHERE DocEntry = #list_of_cols_val_tab_del
DECLARE #CallIDo13 VARCHAR(10) = (SELECT TOP 1 [U_HEATID] FROM OINV WHERE [DocEntry] = #list_of_cols_val_tab_del);
DECLARE #DocNumo13 VARCHAR(MAX) = (SELECT TOP 1 [DocNum] FROM OINV WHERE [DocEntry] = #list_of_cols_val_tab_del);
EXEC [AFSJHBSQL01].[HEAT].[dbo].[uspCreateJournalFromINV] #CallIDo13, #DocNumo13, #object_type;
END
ELSE IF #object_type = 14 AND #transaction_type = 'A'
BEGIN
UPDATE RIN1
SET U_jnl1='N', U_jnl2='N', U_jnl3='N', U_jnl4='N'
WHERE DocEntry = #list_of_cols_val_tab_del
END
ELSE IF #object_type = 15 AND #transaction_type = 'A'
BEGIN
UPDATE DLN1
SET U_jnl1='N', U_jnl2='N', U_jnl3='N', U_jnl4='N'
WHERE DocEntry = #list_of_cols_val_tab_del
DECLARE #CallIDo15 VARCHAR(10) = (SELECT TOP 1 [U_HEATID] FROM ODLN WHERE DocEntry = #list_of_cols_val_tab_del);
DECLARE #DocNumo15 VARCHAR(MAX) = (SELECT TOP 1 [DocNum] FROM ODLN WHERE DocEntry = #list_of_cols_val_tab_del);
EXEC [AFSJHBSQL01].[HEAT].[dbo].[uspCreateJournalFromDN] #CallIDo15, #DocNumo15, #object_type;
END
ELSE IF #object_type = 16 AND #transaction_type = 'A'
BEGIN
UPDATE RDN1
SET U_jnl1='N', U_jnl2='N', U_jnl3='N', U_jnl4='N'
WHERE DocEntry = #list_of_cols_val_tab_del
END
ELSE IF #object_type = 17 AND #transaction_type = 'A'
BEGIN
DECLARE #ItemCode VARCHAR(MAX) = (SELECT TOP 1 [ItemCode] FROM [RDR1] WHERE [DocEntry] = #list_of_cols_val_tab_del AND ([ItemCode] = 'VIU Chip Prod' OR [ItemCode] = 'FPR Chip Production'));
DECLARE #Desc VARCHAR(MAX) = (SELECT TOP 1 [CardName] FROM ORDR WHERE [DocEntry] = #list_of_cols_val_tab_del);
--DECLARE #CallID VARCHAR(10) = (SELECT TOP 1 [U_HEATID] FROM ORDR WHERE [DocEntry] = #list_of_cols_val_tab_del);
IF (#ItemCode = 'VIU Chip Prod')
BEGIN
EXEC [AFSJHBSQL01].[HEAT].[dbo].[uspCreateHeatJobFromSO] #list_of_cols_val_tab_del, #ItemCode, #Desc;
--RETURN;
END
ELSE IF(#ItemCode = 'FPR Chip Production')
BEGIN
EXEC [AFSJHBSQL01].[HEAT].[dbo].[uspCreateHeatJobFromSO] #list_of_cols_val_tab_del, #ItemCode, #Desc;
--RETURN;
END
END
--------------------------------------------------------------------------------------------------------------------------------
-- Select the return values
select #error, #error_message
END

Wrapper stored procedure to create publication for transactional replication

I have created a wrapper stored procedure to create publication for transactional replication for SQL Server 2008 Standard Edition SP3. But when I execute the procedure I get the following error. The column "file_exists" is not user defined. This error doesn't make any sense to me. This works on Dev environment but same code doesn't work on test environment. Dev and Test are identical as far as I can tell. I also tried to explicitly set options, made them 5496 (SELECT ##OPTIONS). Any help greatly appreciated.
-- error
Msg 50000, Level 16, State 1, Procedure CreatePublicationForDB, Line 161
Invalid column name 'file_exists'.
-- Begin Script
CREATE DATABASE TestPublication
GO
USE TestPublication
CREATE TABLE Orders(
OrderID INT PRIMARY KEY,
CustomerID INT,
ProductID INT,
UpdatedAt DATETIME,
UpdatedBy DATETIME
)
GO
CREATE TABLE Products(
ProductID INT PRIMARY KEY,
ProductName VARCHAR(100)
)
GO
CREATE VIEW V_Order
AS
SELECT o.OrderID,o.CustomerID, p.ProductName
FROM Orders o
JOIN Products p
ON o.ProductID = p.ProductID
GO
CREATE SCHEMA repl
GO
CREATE TABLE repl.ReplicationTables
(
DBName sys.sysname NOT NULL DEFAULT('TestPublication'),
SchemaOwner sys.sysname NOT NULL DEFAULT('dbo'),
TableName sys.sysname NOT NULL
)
GO
INSERT INTO repl.ReplicationTables (tablename)
VALUES('Orders'),('Products'),('V_Order')
GO
USE TestPublication
GO
CREATE PROCEDURE CreatePublicationForDB( #databaseName sysname, #publicationName sysname = #databaseName, #allow_initialize_from_backup NVARCHAR(5) = 'true')
AS
BEGIN
BEGIN TRY
SET ANSI_WARNINGS ON
SET ANSI_PADDING ON
SET ANSI_NULLS ON
SET ARITHABORT ON
SET QUOTED_IDENTIFIER ON
SET ANSI_NULL_DFLT_ON ON
SET CONCAT_NULL_YIELDS_NULL ON
DECLARE #sp_replicationdboption varchar(MAX) = ' USE '+#databaseName +';',
#sp_addpulication VARCHAR(MAX) = ' USE '+#databaseName +';',
#sp_addpublication_snapshot VARCHAR(MAX) = ' USE '+#databaseName +';',
#sp_addarticle VARCHAR(MAX) = ' USE '+#databaseName +';',
#sp_startpublication_snapshot VARCHAR(MAX) = ' USE '+#databaseName +';'
DECLARE #allow_anonymous NVARCHAR(5) = CASE WHEN #allow_initialize_from_backup = 'false' OR #allow_initialize_from_backup IS NULL THEN 'true' ELSE 'false' END
DECLARE #immediate_sync NVARCHAR(5) = #allow_anonymous, #publisher sysname = ##SERVERNAME
-- set up database publication
SET #sp_replicationdboption += '
exec sp_replicationdboption #dbname = N'''+#databaseName+ ''',
#optname = N''publish'',
#value = N''true'''
-- Publication
SET #sp_addpulication += '
exec sp_addpublication #publication = N'''+#publicationName+ ''',
#description = N''Transactional publication of database '+#databaseName+' from Publisher '+#publisher+''',
#sync_method = N''concurrent'',
#retention = 0,
#allow_push = N''true'',
#allow_pull = N''true'',
#allow_anonymous = N'''+#allow_anonymous+ ''' ,
#enabled_for_internet = N''false'',
#snapshot_in_defaultfolder = N''true'',
#compress_snapshot = N''false'',
#ftp_port = 21,
#ftp_login = N''anonymous'',
#allow_subscription_copy = N''false'',
#add_to_active_directory = N''false'',
#repl_freq = N''continuous'',
#status = N''active'',
#independent_agent = N''true'',
#immediate_sync = N'''+#immediate_sync+ ''' ,
#allow_sync_tran = N''false'',
#autogen_sync_procs = N''false'',
#allow_queued_tran = N''false'',
#allow_dts = N''false'',
#replicate_ddl = 1,
#allow_initialize_from_backup = N'''+COALESCE(#allow_initialize_from_backup, 'false')+ ''' ,
#enabled_for_p2p = N''false'',
#enabled_for_het_sub = N''false'''
IF #allow_initialize_from_backup = 'false'
BEGIN
-- publication snapshot
SET #sp_addpublication_snapshot +='
exec sp_addpublication_snapshot #publication = N'''+#publicationName+ ''',
#frequency_type = 1,
#frequency_interval = 0,
#frequency_relative_interval = 0,
#frequency_recurrence_factor = 0,
#frequency_subday = 0,
#frequency_subday_interval = 0,
#active_start_time_of_day = 0,
#active_end_time_of_day = 235959,
#active_start_date = 0,
#active_end_date = 0,
#job_login = null,
#job_password = null,
#publisher_security_mode = 1'
SET #sp_startpublication_snapshot+=' exec sys.sp_startpublication_snapshot #publication = N'''+#publicationName+ ''''
END
-- Articles
IF OBJECT_ID('tempdb..#t') IS NULL
BEGIN
PRINT 'creating temp table t'
CREATE TABLE #t (NAME sysname,objectid INT, sch_owner sysname NULL, article sysname NOT NULL, isIndexed BIT NULL, IsSchemaBound BIT NULL, TYPE CHAR(2) NULL)
END
INSERT INTO #t(NAME,objectid, sch_owner,isIndexed,IsSchemaBound, TYPE,article)
EXEC('
USE '+#databaseName + '
SELECT f.Name, f.object_id,f.sch, f.IsIndexed,f.IsSchemaBound, f.type,CASE WHEN ROW_NUMBER() OVER (PARTITION BY f.name ORDER BY f.sch) > 1 THEN f.name + CAST((ROW_NUMBER() OVER (PARTITION BY f.name ORDER BY f.sch) - 1) AS VARCHAR(2)) ELSE f.name END AS Article
FROM(
SELECT t.Name, t.object_id,t.sch, IsIndexed,IsSchemaBound, type
FROM
(SELECT DBName, SchemaOwner, TableName
FROM TestPublication.repl.ReplicationTables
GROUP BY DBName, SchemaOwner, TableName )rt JOIN
(SELECT o.Name, o.object_id,s.name AS sch, objectproperty(o.object_id, ''IsIndexed'') AS IsIndexed,objectproperty(o.object_id, ''IsSchemaBound'') AS IsSchemaBound, o.type
FROM
sys.objects o
JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE o.type IN (''U'',''V'')
AND ObjectProperty(o.object_id, ''IsMSShipped'') = 0
AND (ObjectProperty(o.object_id, ''TableHasPrimaryKey'') = 1 OR ObjectProperty(o.object_id, ''TableHasPrimaryKey'') IS NULL)
) t ON rt.tablename = t.name AND rt.SchemaOwner = t.sch
WHERE rt.DBName = '''+#databaseName + '''
) f'
)
SELECT #sp_addarticle +=
'exec sp_addarticle
#publication = N''' +#databaseName +
''', #article = N''' +t.article+
''', #source_owner = N''' +t.sch_owner +
''', #source_object = N''' + t.NAME +
''', #type = N''' +
CASE WHEN t.type = 'U' THEN 'logbased'
WHEN t.type = 'V' AND (IsIndexed = 1 OR IsSchemaBound = 1 )THEN 'indexed view schema only'
WHEN t.type = 'V' AND IsIndexed = 0 THEN 'view schema only' END
+''', #description = null,#creation_script = null,#pre_creation_cmd = N''drop'',
#schema_option = '+
CASE WHEN t.type = 'U' THEN '0x000000000803509F'
WHEN t.type = 'V' THEN '0x0000000008000001' END+
',#destination_table = N'''+t.Name+
''',#destination_owner = N'''+t.sch_owner+''''+
CASE WHEN t.TYPE = 'U' THEN
', #identityrangemanagementoption = N''manual'',#vertical_partition = N''false'',
#ins_cmd = N''CALL sp_MSins_'+t.sch_owner+''+t.Name+
''', #del_cmd = N''CALL sp_MSdel_'+t.sch_owner+''+t.Name+''',
#upd_cmd = N''SCALL sp_MSupd_'+t.sch_owner+''+t.Name+''''
ELSE ''
END
+';'
FROM #t t
PRINT 'Now running sp_replicationdboption'
PRINT #sp_replicationdboption
EXEC(#sp_replicationdboption)
PRINT 'Now running sp_addpulication'
PRINT #sp_addpulication
EXEC(#sp_addpulication)
IF #allow_initialize_from_backup = 'false'
BEGIN
PRINT 'Now running sp_addpulication_snapshot and starting snapshot'
PRINT #sp_addpublication_snapshot
EXEC(#sp_addpublication_snapshot)
EXEC(#sp_startpublication_snapshot)
END
PRINT 'Now running sp_addarticles'
PRINT #sp_addarticle
EXEC(#sp_addarticle)
-- exec sp_droppublication #publication = N'Products'
END TRY
BEGIN CATCH
IF ##trancount > 0
ROLLBACK
DECLARE #ERROR_SEVERITY INT, #ERROR_STATE INT, #ERROR_MESSAGE NVARCHAR(4000)
SELECT #ERROR_SEVERITY = ERROR_SEVERITY(), #ERROR_STATE = ERROR_STATE(), #ERROR_MESSAGE = ERROR_MESSAGE()
RAISERROR(#ERROR_MESSAGE, #ERROR_SEVERITY, #ERROR_STATE)
END CATCH
END
GO
and finally execute
EXEC CreatePublicationForDB 'TestPublication'
-- drop replication in case you want to run the above again.
exec TestPublication.dbo.sp_droppublication #publication = 'TestPublication'
exec TestPublication.dbo.sp_replicationdboption #dbname = 'TestPublication', #optname = 'publish', #value = 'false'
-- cleanup database
DROP DATABASE TestPublication
Sorry, old post I know, but I ran into a very similar situation and got around it. I haven't found a workable solution anywhere else. I'm including my experience to help others.
Summary of my situation is that I have an msbuild process running a series of scripts from a manifest file (text file with a series of script names) within a transaction. The scripts that were intended to create and configure agent jobs always died with the same error. (invalid column name "file_exists")
Procs used in my failing script(s):
msdb.dbo.sp_add_category
msdb.dbo.sp_add_job
msdb.dbo.sp_add_jobstep
msdb.dbo.sp_update_job
msdb.dbo.sp_add_jobschedule
msdb.dbo.sp_add_jobserver
Commenting out the call to msdb.dbo.sp_add_jobstep allowed the script to complete.
I do have SQL command variables in the form #database_name=N'$(DatabaseName)' in the script as well. This led to some misdirected efforts to try to use escape macros as mentioned in the "Important" note under [ #command= ] 'command' on the documentation for sp_add_jobstep.
https://msdn.microsoft.com/en-us/library/ms187358.aspx
If I run the build up to, but not including, the job creation script and start over, it succeeds.
What I found, after much trial and error, was that, even though the procs are qualified as being in [MSDB], unless I actually included a USE statement, the process fails. By beginning the script with "USE [MSDB] GO" and then switching back to my build target (i.e. USE [MyBuildDB] GO), I no longer get the error.
My agent jobs create as expected, and several scripts within the build run without error.
It's hard to say what the problem is without seeing all of the code and having the schema. I'd recommend not creating a wrapper to create publication(s). The create publication scripts can be saved and executed on demand when needed - eliminating the need for a wrapper.

Resources