Replicating Jobs Between SQL Instances – Jobs

By Greg No comments

This is part 5 in the series of how to replicate SQL Jobs between instances and we’re finally up to the part that actually replicates the jobs. To get the background and all the steps you need to do before this one, go and read the other posts first.

Replicating Jobs has taken a lot of trial and error work (and a lot of alerts to the rest of the team that the job was failing) before I got it right. There are a number of scenarios that we have to deal with:

  • A brand new job
  • A new step in an existing job
  • A deleted step from an existing job
  • A change in the job description
  • A change in the job step
  • Probably something else

As mentioned in a previous post, we’re not going to automatically delete jobs off secondary servers.

There is a lot of code to do a few simple things. Basically we’re going to get a list of jobs that need inserting or updating, and then cursor through all the jobs and their steps and insert/update/delete(steps) as required.

1. Setup

This is the same as the setup for categories. The script checks that it isn’t running on the primary server, creates a variable with the name of the primary server and creates a temp table.

SET NOCOUNT ON

IF 0 = (
    SELECT master.dbo.fCheckIfPrimary('PrimaryReplicaName')
    )
BEGIN
  DECLARE @PartnerServer SYSNAME = 'PrimaryReplicaName'
  DECLARE @Machine SYSNAME
  DECLARE @SQL NVARCHAR(MAX)

  IF CharIndex('', @PartnerServer) > 0
  BEGIN
    SET @Machine = LEFT(@PartnerServer, CharIndex('', @PartnerServer) - 1);
  END
  ELSE
  BEGIN
    SET @Machine = @PartnerServer;
  END

  CREATE TABLE #Jobs (
    job_id UNIQUEIDENTIFIER
    ,Action VARCHAR(6)
    ,[originating_server_id] [int] NOT NULL
    ,[name] [sysname] NOT NULL
    ,[enabled] [tinyint] NOT NULL
    ,[description] [nvarchar](512) NULL
    ,[start_step_id] [int] NOT NULL
    ,[category_id] [int] NOT NULL
    ,[categoryname] SYSNAME NULL
    ,[owner_sid] [varbinary](85) NOT NULL
    ,[owner_name] SYSNAME NULL
    ,[notify_level_eventlog] [int] NOT NULL
    ,[notify_level_email] [int] NOT NULL
    ,[notify_level_netsend] [int] NOT NULL
    ,[notify_level_page] [int] NOT NULL
    ,[notify_email_operator_id] [int] NOT NULL
    ,[notify_netsend_operator_id] [int] NOT NULL
    ,[notify_page_operator_id] [int] NOT NULL
    ,[Notify_Email_Operator_name] SYSNAME NULL
    ,[delete_level] [int] NOT NULL
    ,[date_created] [datetime] NOT NULL
    ,[date_modified] [datetime] NOT NULL
    ,[version_number] [int] NOT NULL
    )

  CREATE TABLE #JobSteps (
    [step_id] [int] NOT NULL
    ,[step_name] [sysname] NOT NULL
    ,[subsystem] [nvarchar](40) NOT NULL
    ,[command] [nvarchar](max) NULL
    ,[additional_parameters] [nvarchar](max) NULL
    ,[cmdexec_success_code] [int] NOT NULL
    ,[on_success_action] [tinyint] NOT NULL
    ,[on_success_step_id] [int] NULL
    ,[on_fail_action] [tinyint] NULL
    ,[on_fail_step_id] [int] NULL
    ,[server] [sysname] NULL
    ,[database_name] [sysname] NULL
    ,[database_user_name] [sysname] NULL
    ,[retry_attempts] [int] NULL
    ,[retry_interval] [int] NULL
    ,[os_run_priority] [int] NULL
    ,[output_file_name] [nvarchar](200) NULL
    ,[flags] [int] NULL
    ,[proxy_id] [int] NULL
    )
    
  --Do Update Jobs
  DROP TABLE #jobSteps
  
  DROP TABLE #Jobs

END

A bit more to setup than other jobs, but still pretty familiar if you’ve been following along. This time 2 temp tables – one for Jobs and one for Steps.

2. Find

Again, pretty similar to the others, just more complex.

SET @SQL = CAST(
    '
      SELECT		*
      FROM
      (
        SELECT		primaryMSDB.Job_id
              ,CASE	WHEN msdbLocal.Job_id IS NULL THEN ''INSERT''
                  WHEN msdbLocal.Job_id IS NOT NULL THEN ''UPDATE''
                  ELSE NULL
              END as Action
              ,primaryMSDB.[originating_server_id]		
              ,primaryMSDB.[name]						
              ,primaryMSDB.[enabled]						
              ,primaryMSDB.[description]					
              ,primaryMSDB.[start_step_id]				
              ,primaryMSDB.[category_id]	
              ,primaryCategories.name as categoryName
              ,primaryMSDB.[owner_sid]
              ,CASE WHEN primaryMSDB.[owner_sid] = 0x01 THEN ''sa'' ELSE primaryPrincipals.name END ownerName
              ,primaryMSDB.[notify_level_eventlog]		
              ,primaryMSDB.[notify_level_email]			
              ,primaryMSDB.[notify_level_netsend]		
              ,primaryMSDB.[notify_level_page]			
              ,primaryMSDB.[notify_email_operator_id]	
              ,primaryMSDB.[notify_netsend_operator_id]	
              ,primaryMSDB.[notify_page_operator_id]
              ,sysoperators.name notifyEmailName
              ,primaryMSDB.[delete_level]				
              ,primaryMSDB.[date_created]				
              ,primaryMSDB.[date_modified]				
              ,primaryMSDB.[version_number]				
        FROM		' 
    + QUOTENAME(@Machine) + '.msdb.dbo.sysJobs as primaryMSDB
        LEFT JOIN	' + QUOTENAME(@Machine) + '.msdb.dbo.sysCategories as primaryCategories
            ON	primaryCategories.category_id = primaryMSDB.category_id
        LEFT JOIN	' + QUOTENAME(@Machine) + '.msdb.dbo.sysoperators
            ON	sysoperators.id = primaryMSDB.notify_email_operator_id
        LEFT JOIN	msdb.dbo.sysJobs msdbLocal
            ON	primaryMSDB.name = msdbLocal.name
        LEFT JOIN	' + QUOTENAME(@Machine) + '.master.sys.database_principals as primaryPrincipals
            ON	primaryPrincipals.sid = primaryMSDB.[owner_sid]
      ) InsertUpdate
      WHERE	Action IS NOT NULL
    ' AS VARCHAR(MAX))

INSERT INTO #Jobs
EXEC sp_ExecuteSQL @SQL

Grab all the jobs that need updating into the #Jobs table. Notice that we are joining based on the name of the job – this assumes that we won’t rename jobs. If we rename a job then we need to delete the original from the secondary servers. For our use case we were happy with this compromise. Also note that all jobs on the primary are going to be selected. There is a case for action being null, but in reality I don’t think it ever can be. We want every job to be included because at this stage we haven’t checked if it has any steps that are different.

3. Copy

Like the others, we’re going to cursor through the records, find what needs updating or inserting and update/insert the job header by calling sp_update_job or sp_add_job

DECLARE @originalJobID UNIQUEIDENTIFIER
  ,@targetJobID UNIQUEIDENTIFIER
  ,@name [sysname]
  ,@enabled [tinyint]
  ,@description [nvarchar] (512)
  ,@start_step_id [int]
  ,@category_id [int]
  ,@owner_sid VARBINARY(85)
  ,@ownerName SYSNAME
  ,@notify_level_eventlog [int]
  ,@notify_level_email [int]
  ,@notify_level_netsend [int]
  ,@notify_level_page [int]
  ,@notify_email_operator_id [int]
  ,@notify_netsend_operator_id [int]
  ,@notify_page_operator_id [int]
  ,@email_operator_name SYSNAME
  ,@delete_level [int]
  ,@date_created [datetime]
  ,@date_modified [datetime]
  ,@version_number [int]
  ,@Action SYSNAME
  ,@CategoryName SYSNAME

DECLARE db_cursor CURSOR
FOR
SELECT job_id
  ,[name]
  ,[enabled]
  ,[description]
  ,[start_step_id]
  ,[category_id]
  ,[categoryname]
  ,[owner_sid]
  ,[owner_name]
  ,[notify_level_eventlog]
  ,[notify_level_email]
  ,[notify_level_netsend]
  ,[notify_level_page]
  ,[notify_email_operator_id]
  ,[notify_netsend_operator_id]
  ,[notify_page_operator_id]
  ,[Notify_Email_Operator_name]
  ,[delete_level]
  ,[date_created]
  ,[date_modified]
  ,[version_number]
  ,Action
FROM #Jobs


OPEN db_cursor

FETCH NEXT
FROM db_cursor
INTO @originalJobID
  ,@name
  ,@enabled
  ,@description
  ,@start_step_id
  ,@category_id
  ,@CategoryName
  ,@owner_sid
  ,@ownerName
  ,@notify_level_eventlog
  ,@notify_level_email
  ,@notify_level_netsend
  ,@notify_level_page
  ,@notify_email_operator_id
  ,@notify_netsend_operator_id
  ,@notify_page_operator_id
  ,@email_operator_name
  ,@delete_level
  ,@date_created
  ,@date_modified
  ,@version_number
  ,@Action

WHILE @@FETCH_STATUS = 0
BEGIN
  IF @Action = 'Update'
  BEGIN
    print 'Updating Job ' + @name
    EXEC msdb.dbo.sp_update_job @job_name = @name
      ,@enabled = @enabled
      ,@description = @description
      ,@start_step_id = @start_step_id
      ,@category_name = @CategoryName
      ,@owner_login_Name = @ownerName
      ,@notify_level_eventlog = @notify_level_eventlog
      ,@notify_level_email = @notify_level_email
      ,@notify_level_netsend = @notify_level_netsend
      ,@notify_level_page = @notify_level_page
      ,@notify_email_operator_name = @email_operator_name
      ,@delete_level = @delete_level

    SELECT @targetJobID = sysjobs.job_id
    FROM msdb.dbo.sysjobs
    WHERE NAME = @name
  END

  IF @Action = 'INSERT'
  BEGIN
    set @targetJobID = null
    print 'Creating Job ' + @name
    print 'Owner: ' + @ownerName

    EXEC msdb.dbo.sp_add_job @job_name = @name
      ,@enabled = @enabled
      ,@description = @description
      ,@start_step_id = @start_step_id
      ,@category_name = @CategoryName
      ,@owner_login_Name = @ownerName
      ,@notify_level_eventlog = @notify_level_eventlog
      ,@notify_level_email = @notify_level_email
      ,@notify_level_netsend = @notify_level_netsend
      ,@notify_level_page = @notify_level_page
      ,@notify_email_operator_name = @email_operator_name
      ,@delete_level = @delete_level
      ,@job_ID = @targetJobID OUTPUT


      EXEC msdb.dbo.sp_add_jobserver @job_id=@targetJobID, @server_name = @@SERVERNAME
    print @targetJobID
    print 'Job Created'
  END

Of significance, and the SQL command I was missing for a long time, was line 123 (highlighted above) that calls sp_add_jobserver. The job inserts fine and runs fine without this step BUT you cannot schedule the job (next blog post) until it has been added to the server. This was a pain to find and I probably should have found it sooner. It would have been nice if sp_add_job included the option to set the jobserver, but it doesn’t.

Once the job headers are copied and we have set @targetJobID we can move on to the steps. The following is a lot of code, but it’s pretty straight forward. We are getting a list of steps, and working out what needs to be added/updated/deleted from the local job. The way this works is on StepID, so in the scenario that you delete say step 2 from a 5 step job on the primary, the secondary will update step 2 with step 3, step 3 with step 4, step 4 with step 5 and delete step 5. It gives you the right steps at the end, but if you keep track of individual steps then you’ll need to work something else out.

DECLARE @step_id [int]
  ,@step_name [sysname]
  ,@subsystem [nvarchar] (40)
  ,@command [nvarchar] (max)
  ,@flags [int]
  ,@additional_parameters [nvarchar] (max)
  ,@cmdexec_success_code [int]
  ,@on_success_action [tinyint]
  ,@on_success_step_id [int]
  ,@on_fail_action [tinyint]
  ,@on_fail_step_id [int]
  ,@server [sysname]
  ,@database_name [sysname]
  ,@database_user_name [sysname]
  ,@retry_attempts [int]
  ,@retry_interval [int]
  ,@os_run_priority [int]
  ,@output_file_name [nvarchar] (200)
  ,@proxy_id [int]

SET @SQL = 'SELECT	step_id,
          step_name,
          subsystem,
          command,
          additional_parameters,
          cmdexec_success_code,
          on_success_action,
          on_success_step_id,
          on_fail_action,
          on_fail_step_id,
          server,
          database_name,
          database_user_name,
          retry_attempts,
          retry_interval,
          os_run_priority,
          output_file_name,
          flags,
          proxy_id
      FROM	' + QUOTENAME(@Machine) + '.msdb.dbo.sysjobsteps 
      WHERE	job_id = ''' + CAST(@originalJobID AS VARCHAR(100)) + ''''

DELETE
FROM #JobSteps



INSERT INTO #JobSteps
EXEC sp_ExecuteSQL @SQL

--select * From #JobSteps

IF (
  SELECT		COUNT(1)
  from		#JobSteps
  LEFT JOIN	msdb.dbo.sysjobsteps
      ON	sysjobsteps.job_id = @targetJobID
      AND sysjobsteps.step_id = #JobSteps.step_id
      AND sysjobsteps.step_name = #JobSteps.step_name
  WHERE		sysjobsteps.step_id IS NULL
  ) > 0

  OR

  (
  SELECT		COUNT(1)
  from		msdb.dbo.sysjobsteps
  LEFT JOIN	#JobSteps
      ON	sysjobsteps.step_id = #JobSteps.step_id
      AND sysjobsteps.step_name = #JobSteps.step_name
  WHERE		sysjobsteps.step_id IS NULL
      AND	sysjobsteps.job_id = @targetJobID
  ) > 0

BEGIN
  DECLARE db_jobStep_todelete_cursor CURSOR
  FOR
  SELECT step_id
  FROM msdb.dbo.sysjobsteps
  WHERE sysjobsteps.job_id = @targetJobID
  order by step_id desc


  OPEN db_jobStep_todelete_cursor

  FETCH NEXT
  FROM db_jobStep_todelete_cursor
  INTO @step_id

  WHILE @@FETCH_STATUS = 0
  BEGIN
    print 'Deleting Step ' + cast(@step_id as varchar(5)) + ' from job ' + @name
    EXEC msdb.dbo.sp_delete_jobstep @job_id = @targetJobID
      ,@step_id = @step_id

    FETCH NEXT
    FROM db_jobStep_todelete_cursor
    INTO @step_id
  END

  CLOSE db_jobStep_todelete_cursor

  DEALLOCATE db_jobStep_todelete_cursor
END

---------------------------
--cursor through all steps for the job and update steps...just do the update
DECLARE db_jobStep_cursor CURSOR
FOR
SELECT step_id
  ,step_name
  ,subsystem
  ,command
  ,flags
  ,additional_parameters
  ,cmdexec_success_code
  ,on_success_action
  ,on_success_step_id
  ,on_fail_action
  ,on_fail_step_id
  ,SERVER
  ,database_name
  ,database_user_name
  ,retry_attempts
  ,retry_interval
  ,os_run_priority
  ,output_file_name
  ,proxy_id
FROM #JobSteps
order by step_id

OPEN db_jobStep_cursor

FETCH NEXT
FROM db_jobStep_cursor
INTO @step_id
  ,@step_name
  ,@subsystem
  ,@command
  ,@flags
  ,@additional_parameters
  ,@cmdexec_success_code
  ,@on_success_action
  ,@on_success_step_id
  ,@on_fail_action
  ,@on_fail_step_id
  ,@server
  ,@database_name
  ,@database_user_name
  ,@retry_attempts
  ,@retry_interval
  ,@os_run_priority
  ,@output_file_name
  ,@proxy_id

WHILE @@FETCH_STATUS = 0
BEGIN
  
  IF EXISTS (
      SELECT 1
      FROM msdb.dbo.sysjobsteps
      WHERE job_id = @targetJobID
        AND step_id = @step_id
      )
  BEGIN
    EXEC msdb.dbo.sp_update_jobstep @job_id = @targetJobID
      ,@step_id = @step_id
      ,@subsystem = @subsystem
      ,@command = @command
      ,@flags = @flags
      ,@additional_parameters = @additional_parameters
      ,@cmdexec_success_code = @cmdexec_success_code
      ,@on_success_action = @on_success_action
      ,@on_success_step_id = @on_success_step_id
      ,@on_fail_action = @on_fail_action
      ,@on_fail_step_id = @on_fail_step_id
      ,@server = @server
      ,@database_name = @database_name
      ,@database_user_name = @database_user_name
      ,@retry_attempts = @retry_attempts
      ,@retry_interval = @retry_interval
      ,@os_run_priority = @os_run_priority
      ,@output_file_name = @output_file_name
      ,@proxy_id = @proxy_id
  END
  ELSE
  BEGIN
    print 'creating' + @step_name
    EXEC msdb.dbo.sp_add_jobstep @job_id = @targetJobID
      ,@step_id = @step_id
      ,@step_name = @step_name
      ,@subsystem = @subsystem
      ,@command = @command
      ,@flags = @flags
      ,@additional_parameters = @additional_parameters
      ,@cmdexec_success_code = @cmdexec_success_code
      ,@on_success_action = @on_success_action
      ,@on_success_step_id = @on_success_step_id
      ,@on_fail_action = @on_fail_action
      ,@on_fail_step_id = @on_fail_step_id
      ,@server = @server
      ,@database_name = @database_name
      ,@database_user_name = @database_user_name
      ,@retry_attempts = @retry_attempts
      ,@retry_interval = @retry_interval
      ,@os_run_priority = @os_run_priority
      ,@output_file_name = @output_file_name
      ,@proxy_id = @proxy_id
  END

  FETCH NEXT
  FROM db_jobStep_cursor
  INTO @step_id
    ,@step_name
    ,@subsystem
    ,@command
    ,@flags
    ,@additional_parameters
    ,@cmdexec_success_code
    ,@on_success_action
    ,@on_success_step_id
    ,@on_fail_action
    ,@on_fail_step_id
    ,@server
    ,@database_name
    ,@database_user_name
    ,@retry_attempts
    ,@retry_interval
    ,@os_run_priority
    ,@output_file_name
    ,@proxy_id
END

CLOSE db_jobStep_cursor

DEALLOCATE db_jobStep_cursor

And 520 lines of SQL later, that’s it! The whole step is posted below. As always, try out code before you use it on your production servers just to make sure it works for your conditions.

This is the hardest part of the replication. The next 2 steps are to join the job to the schedules and to disable jobs that we don’t want running (SSRS jobs).

SET NOCOUNT ON

IF 0 = (
    SELECT master.dbo.fCheckIfPrimary('SQLAAG_ENT_PROD')
    )
BEGIN
  DECLARE @PartnerServer SYSNAME = 'SQLAAG_ENT_PROD'
  DECLARE @Machine SYSNAME
  DECLARE @SQL NVARCHAR(MAX)

  IF CharIndex('', @PartnerServer) > 0
  BEGIN
    SET @Machine = LEFT(@PartnerServer, CharIndex('', @PartnerServer) - 1);
  END
  ELSE
  BEGIN
    SET @Machine = @PartnerServer;
  END

  CREATE TABLE #Jobs (
    job_id UNIQUEIDENTIFIER
    ,Action VARCHAR(6)
    ,[originating_server_id] [int] NOT NULL
    ,[name] [sysname] NOT NULL
    ,[enabled] [tinyint] NOT NULL
    ,[description] [nvarchar](512) NULL
    ,[start_step_id] [int] NOT NULL
    ,[category_id] [int] NOT NULL
    ,[categoryname] SYSNAME NULL
    ,[owner_sid] [varbinary](85) NOT NULL
    ,[owner_name] SYSNAME NULL
    ,[notify_level_eventlog] [int] NOT NULL
    ,[notify_level_email] [int] NOT NULL
    ,[notify_level_netsend] [int] NOT NULL
    ,[notify_level_page] [int] NOT NULL
    ,[notify_email_operator_id] [int] NOT NULL
    ,[notify_netsend_operator_id] [int] NOT NULL
    ,[notify_page_operator_id] [int] NOT NULL
    ,[Notify_Email_Operator_name] SYSNAME NULL
    ,[delete_level] [int] NOT NULL
    ,[date_created] [datetime] NOT NULL
    ,[date_modified] [datetime] NOT NULL
    ,[version_number] [int] NOT NULL
    )

  CREATE TABLE #JobSteps (
    [step_id] [int] NOT NULL
    ,[step_name] [sysname] NOT NULL
    ,[subsystem] [nvarchar](40) NOT NULL
    ,[command] [nvarchar](max) NULL
    ,[additional_parameters] [nvarchar](max) NULL
    ,[cmdexec_success_code] [int] NOT NULL
    ,[on_success_action] [tinyint] NOT NULL
    ,[on_success_step_id] [int] NULL
    ,[on_fail_action] [tinyint] NULL
    ,[on_fail_step_id] [int] NULL
    ,[server] [sysname] NULL
    ,[database_name] [sysname] NULL
    ,[database_user_name] [sysname] NULL
    ,[retry_attempts] [int] NULL
    ,[retry_interval] [int] NULL
    ,[os_run_priority] [int] NULL
    ,[output_file_name] [nvarchar](200) NULL
    ,[flags] [int] NULL
    ,[proxy_id] [int] NULL
    )

    
  SET @SQL = CAST(
      '
        SELECT		*
        FROM
        (
          SELECT		primaryMSDB.Job_id
                ,CASE	WHEN msdbLocal.Job_id IS NULL THEN ''INSERT''
                    WHEN msdbLocal.Job_id IS NOT NULL THEN ''UPDATE''
                    ELSE NULL
                END as Action
                ,primaryMSDB.[originating_server_id]		
                ,primaryMSDB.[name]						
                ,primaryMSDB.[enabled]						
                ,primaryMSDB.[description]					
                ,primaryMSDB.[start_step_id]				
                ,primaryMSDB.[category_id]	
                ,primaryCategories.name as categoryName
                ,primaryMSDB.[owner_sid]
                ,CASE WHEN primaryMSDB.[owner_sid] = 0x01 THEN ''sa'' ELSE primaryPrincipals.name END ownerName
                ,primaryMSDB.[notify_level_eventlog]		
                ,primaryMSDB.[notify_level_email]			
                ,primaryMSDB.[notify_level_netsend]		
                ,primaryMSDB.[notify_level_page]			
                ,primaryMSDB.[notify_email_operator_id]	
                ,primaryMSDB.[notify_netsend_operator_id]	
                ,primaryMSDB.[notify_page_operator_id]
                ,sysoperators.name notifyEmailName
                ,primaryMSDB.[delete_level]				
                ,primaryMSDB.[date_created]				
                ,primaryMSDB.[date_modified]				
                ,primaryMSDB.[version_number]				
          FROM		' 
      + QUOTENAME(@Machine) + '.msdb.dbo.sysJobs as primaryMSDB
          LEFT JOIN	' + QUOTENAME(@Machine) + '.msdb.dbo.sysCategories as primaryCategories
              ON	primaryCategories.category_id = primaryMSDB.category_id
          LEFT JOIN	' + QUOTENAME(@Machine) + '.msdb.dbo.sysoperators
              ON	sysoperators.id = primaryMSDB.notify_email_operator_id
          LEFT JOIN	msdb.dbo.sysJobs msdbLocal
              ON	primaryMSDB.name = msdbLocal.name
          LEFT JOIN	' + QUOTENAME(@Machine) + '.master.sys.database_principals as primaryPrincipals
              ON	primaryPrincipals.sid = primaryMSDB.[owner_sid]
        ) InsertUpdate
        WHERE	Action IS NOT NULL
      ' AS VARCHAR(MAX))

  INSERT INTO #Jobs
  EXEC sp_ExecuteSQL @SQL

  --select * from #Jobs
  
  DECLARE @originalJobID UNIQUEIDENTIFIER
    ,@targetJobID UNIQUEIDENTIFIER
    ,@name [sysname]
    ,@enabled [tinyint]
    ,@description [nvarchar] (512)
    ,@start_step_id [int]
    ,@category_id [int]
    ,@owner_sid VARBINARY(85)
    ,@ownerName SYSNAME
    ,@notify_level_eventlog [int]
    ,@notify_level_email [int]
    ,@notify_level_netsend [int]
    ,@notify_level_page [int]
    ,@notify_email_operator_id [int]
    ,@notify_netsend_operator_id [int]
    ,@notify_page_operator_id [int]
    ,@email_operator_name SYSNAME
    ,@delete_level [int]
    ,@date_created [datetime]
    ,@date_modified [datetime]
    ,@version_number [int]
    ,@Action SYSNAME
    ,@CategoryName SYSNAME

  DECLARE db_cursor CURSOR
  FOR
  SELECT job_id
    ,[name]
    ,[enabled]
    ,[description]
    ,[start_step_id]
    ,[category_id]
    ,[categoryname]
    ,[owner_sid]
    ,[owner_name]
    ,[notify_level_eventlog]
    ,[notify_level_email]
    ,[notify_level_netsend]
    ,[notify_level_page]
    ,[notify_email_operator_id]
    ,[notify_netsend_operator_id]
    ,[notify_page_operator_id]
    ,[Notify_Email_Operator_name]
    ,[delete_level]
    ,[date_created]
    ,[date_modified]
    ,[version_number]
    ,Action
  FROM #Jobs
  

  OPEN db_cursor

  FETCH NEXT
  FROM db_cursor
  INTO @originalJobID
    ,@name
    ,@enabled
    ,@description
    ,@start_step_id
    ,@category_id
    ,@CategoryName
    ,@owner_sid
    ,@ownerName
    ,@notify_level_eventlog
    ,@notify_level_email
    ,@notify_level_netsend
    ,@notify_level_page
    ,@notify_email_operator_id
    ,@notify_netsend_operator_id
    ,@notify_page_operator_id
    ,@email_operator_name
    ,@delete_level
    ,@date_created
    ,@date_modified
    ,@version_number
    ,@Action

  WHILE @@FETCH_STATUS = 0
  BEGIN
    IF @Action = 'Update'
    BEGIN
      print 'Updating Job ' + @name
      EXEC msdb.dbo.sp_update_job @job_name = @name
        ,@enabled = @enabled
        ,@description = @description
        ,@start_step_id = @start_step_id
        ,@category_name = @CategoryName
        ,@owner_login_Name = @ownerName
        ,@notify_level_eventlog = @notify_level_eventlog
        ,@notify_level_email = @notify_level_email
        ,@notify_level_netsend = @notify_level_netsend
        ,@notify_level_page = @notify_level_page
        ,@notify_email_operator_name = @email_operator_name
        ,@delete_level = @delete_level

      SELECT @targetJobID = sysjobs.job_id
      FROM msdb.dbo.sysjobs
      WHERE NAME = @name
    END

    IF @Action = 'INSERT'
    BEGIN
      set @targetJobID = null
      print 'Creating Job ' + @name
      print 'Owner: ' + @ownerName

      EXEC msdb.dbo.sp_add_job @job_name = @name
        ,@enabled = @enabled
        ,@description = @description
        ,@start_step_id = @start_step_id
        ,@category_name = @CategoryName
        ,@owner_login_Name = @ownerName
        ,@notify_level_eventlog = @notify_level_eventlog
        ,@notify_level_email = @notify_level_email
        ,@notify_level_netsend = @notify_level_netsend
        ,@notify_level_page = @notify_level_page
        ,@notify_email_operator_name = @email_operator_name
        ,@delete_level = @delete_level
        ,@job_ID = @targetJobID OUTPUT


        EXEC msdb.dbo.sp_add_jobserver @job_id=@targetJobID, @server_name = @@SERVERNAME
      print @targetJobID
      print 'Job Created'
    END

    DECLARE @step_id [int]
      ,@step_name [sysname]
      ,@subsystem [nvarchar] (40)
      ,@command [nvarchar] (max)
      ,@flags [int]
      ,@additional_parameters [nvarchar] (max)
      ,@cmdexec_success_code [int]
      ,@on_success_action [tinyint]
      ,@on_success_step_id [int]
      ,@on_fail_action [tinyint]
      ,@on_fail_step_id [int]
      ,@server [sysname]
      ,@database_name [sysname]
      ,@database_user_name [sysname]
      ,@retry_attempts [int]
      ,@retry_interval [int]
      ,@os_run_priority [int]
      ,@output_file_name [nvarchar] (200)
      ,@proxy_id [int]

    SET @SQL = 'SELECT	step_id,
              step_name,
              subsystem,
              command,
              additional_parameters,
              cmdexec_success_code,
              on_success_action,
              on_success_step_id,
              on_fail_action,
              on_fail_step_id,
              server,
              database_name,
              database_user_name,
              retry_attempts,
              retry_interval,
              os_run_priority,
              output_file_name,
              flags,
              proxy_id
          FROM	' + QUOTENAME(@Machine) + '.msdb.dbo.sysjobsteps 
          WHERE	job_id = ''' + CAST(@originalJobID AS VARCHAR(100)) + ''''

    DELETE
    FROM #JobSteps



    INSERT INTO #JobSteps
    EXEC sp_ExecuteSQL @SQL

    --select * From #JobSteps

    IF (
      SELECT		COUNT(1)
      from		#JobSteps
      LEFT JOIN	msdb.dbo.sysjobsteps
          ON	sysjobsteps.job_id = @targetJobID
          AND sysjobsteps.step_id = #JobSteps.step_id
          AND sysjobsteps.step_name = #JobSteps.step_name
      WHERE		sysjobsteps.step_id IS NULL
      ) > 0

      OR

      (
      SELECT		COUNT(1)
      from		msdb.dbo.sysjobsteps
      LEFT JOIN	#JobSteps
          ON	sysjobsteps.step_id = #JobSteps.step_id
          AND sysjobsteps.step_name = #JobSteps.step_name
      WHERE		sysjobsteps.step_id IS NULL
          AND	sysjobsteps.job_id = @targetJobID
      ) > 0

    BEGIN
      DECLARE db_jobStep_todelete_cursor CURSOR
      FOR
      SELECT step_id
      FROM msdb.dbo.sysjobsteps
      WHERE sysjobsteps.job_id = @targetJobID
      order by step_id desc


      OPEN db_jobStep_todelete_cursor

      FETCH NEXT
      FROM db_jobStep_todelete_cursor
      INTO @step_id

      WHILE @@FETCH_STATUS = 0
      BEGIN
        print 'Deleting Step ' + cast(@step_id as varchar(5)) + ' from job ' + @name
        EXEC msdb.dbo.sp_delete_jobstep @job_id = @targetJobID
          ,@step_id = @step_id

        FETCH NEXT
        FROM db_jobStep_todelete_cursor
        INTO @step_id
      END

      CLOSE db_jobStep_todelete_cursor

      DEALLOCATE db_jobStep_todelete_cursor
    END
    
    ---------------------------
    --cursor through all steps for the job and update steps...just do the update
    DECLARE db_jobStep_cursor CURSOR
    FOR
    SELECT step_id
      ,step_name
      ,subsystem
      ,command
      ,flags
      ,additional_parameters
      ,cmdexec_success_code
      ,on_success_action
      ,on_success_step_id
      ,on_fail_action
      ,on_fail_step_id
      ,SERVER
      ,database_name
      ,database_user_name
      ,retry_attempts
      ,retry_interval
      ,os_run_priority
      ,output_file_name
      ,proxy_id
    FROM #JobSteps
    order by step_id

    OPEN db_jobStep_cursor

    FETCH NEXT
    FROM db_jobStep_cursor
    INTO @step_id
      ,@step_name
      ,@subsystem
      ,@command
      ,@flags
      ,@additional_parameters
      ,@cmdexec_success_code
      ,@on_success_action
      ,@on_success_step_id
      ,@on_fail_action
      ,@on_fail_step_id
      ,@server
      ,@database_name
      ,@database_user_name
      ,@retry_attempts
      ,@retry_interval
      ,@os_run_priority
      ,@output_file_name
      ,@proxy_id

    WHILE @@FETCH_STATUS = 0
    BEGIN
      
      IF EXISTS (
          SELECT 1
          FROM msdb.dbo.sysjobsteps
          WHERE job_id = @targetJobID
            AND step_id = @step_id
          )
      BEGIN
        EXEC msdb.dbo.sp_update_jobstep @job_id = @targetJobID
          ,@step_id = @step_id
          ,@subsystem = @subsystem
          ,@command = @command
          ,@flags = @flags
          ,@additional_parameters = @additional_parameters
          ,@cmdexec_success_code = @cmdexec_success_code
          ,@on_success_action = @on_success_action
          ,@on_success_step_id = @on_success_step_id
          ,@on_fail_action = @on_fail_action
          ,@on_fail_step_id = @on_fail_step_id
          ,@server = @server
          ,@database_name = @database_name
          ,@database_user_name = @database_user_name
          ,@retry_attempts = @retry_attempts
          ,@retry_interval = @retry_interval
          ,@os_run_priority = @os_run_priority
          ,@output_file_name = @output_file_name
          ,@proxy_id = @proxy_id
      END
      ELSE
      BEGIN
        print 'creating' + @step_name
        EXEC msdb.dbo.sp_add_jobstep @job_id = @targetJobID
          ,@step_id = @step_id
          ,@step_name = @step_name
          ,@subsystem = @subsystem
          ,@command = @command
          ,@flags = @flags
          ,@additional_parameters = @additional_parameters
          ,@cmdexec_success_code = @cmdexec_success_code
          ,@on_success_action = @on_success_action
          ,@on_success_step_id = @on_success_step_id
          ,@on_fail_action = @on_fail_action
          ,@on_fail_step_id = @on_fail_step_id
          ,@server = @server
          ,@database_name = @database_name
          ,@database_user_name = @database_user_name
          ,@retry_attempts = @retry_attempts
          ,@retry_interval = @retry_interval
          ,@os_run_priority = @os_run_priority
          ,@output_file_name = @output_file_name
          ,@proxy_id = @proxy_id
      END

      FETCH NEXT
      FROM db_jobStep_cursor
      INTO @step_id
        ,@step_name
        ,@subsystem
        ,@command
        ,@flags
        ,@additional_parameters
        ,@cmdexec_success_code
        ,@on_success_action
        ,@on_success_step_id
        ,@on_fail_action
        ,@on_fail_step_id
        ,@server
        ,@database_name
        ,@database_user_name
        ,@retry_attempts
        ,@retry_interval
        ,@os_run_priority
        ,@output_file_name
        ,@proxy_id
    END

    CLOSE db_jobStep_cursor

    DEALLOCATE db_jobStep_cursor


    

    ----------------------------
    FETCH NEXT
    FROM db_cursor
    INTO @originalJobID
      ,@name
      ,@enabled
      ,@description
      ,@start_step_id
      ,@category_id
      ,@CategoryName
      ,@owner_sid
      ,@ownerName
      ,@notify_level_eventlog
      ,@notify_level_email
      ,@notify_level_netsend
      ,@notify_level_page
      ,@notify_email_operator_id
      ,@notify_netsend_operator_id
      ,@notify_page_operator_id
      ,@email_operator_name
      ,@delete_level
      ,@date_created
      ,@date_modified
      ,@version_number
      ,@Action
  END

  CLOSE db_cursor

  DEALLOCATE db_cursor

  DROP TABLE #jobSteps
  
  DROP TABLE #Jobs

END

 

 

Leave a Reply