Search code examples
sql-serverstored-proceduressqlcmdsql-job

Running and checking the result of a sql agent job through a shell script


Basically I need to create a shell script to:

  • run a SQL agent job on a remote server
  • wait for it to run
  • check for the completion status and be able to save it / output it so it can be used further

The way I'm planning it is to create a stored proc with a job name parameter like this:

-- Start job
EXEC msdb.dbo.sp_start_job @job_name = @job_name

-- Wait for Job to finish
DECLARE @job_status INT
SELECT @job_status = current_execution_status FROM OPENROWSET('SQLNCLI', 'Server=<server>;','exec msdb.dbo.sp_help_job @job_name = @job_name ')

WHILE @job_status <> 4
BEGIN
    WAITFOR DELAY '00:00:03'
    SELECT @job_status = current_execution_status FROM OPENROWSET('SQLNCLI', 'Server=<server>;','exec msdb.dbo.sp_help_job @job_name = @job_name')
END

-- Get Result

SELECT TOP 1 @job_history_id = activity.job_history_id
    FROM msdb.dbo.sysjobs jobs
    INNER JOIN msdb.dbo.sysjobactivity activity ON activity.job_id = jobs.job_id
    WHERE jobs.name = @job_name
    ORDER BY activity.start_execution_date DESC

SELECT history.run_status
FROM msdb.dbo.sysjobhistory history
WHERE history.instance_id = @job_history_id

Ideally, I could run every SQL agent job through this stored_proc so I can very easily call this stored_proc via sqlcmd with the job name as a parameter and save the output as the result of the job.
As I'm not the most experienced with SQL Server, I come to you all for help.

  1. Is my stored proc efficient/functional? can anyone enlighten me to a better way of doing this?
  2. Should this stored proc be created on the msdb database and if so should it be saved as a system object ?
  3. Is this going to post a problem with migrations or any management (i.e. special actions to migrate SQL versions / to different servers in order to keep this stored proc)?

Thanks in advance for any support.


Solution

  • For Anyone looking for the answer to this, I came to a stored procedure that works quite well:

    CREATE PROCEDURE dbo.sp_sp_start_job_wait
    (
    @job_name SYSNAME,
    @WaitTime DATETIME = '00:00:10', --check frequency
    @JobCompletionStatus INT = null OUTPUT
    )
    AS
    
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    SET NOCOUNT ON
    
    DECLARE @job_id UNIQUEIDENTIFIER
    DECLARE @job_owner sysname
    
    -- Create temp table with xp_sqlagent_enum_jobs structure
    CREATE TABLE #xp_results (job_id UNIQUEIDENTIFIER NOT NULL,
    last_run_date INT NOT NULL,
    last_run_time INT NOT NULL,
    next_run_date INT NOT NULL,
    next_run_time INT NOT NULL,
    next_run_schedule_id INT NOT NULL,
    requested_to_run INT NOT NULL, -- BOOL
    request_source INT NOT NULL,
    request_source_id sysname COLLATE database_default NULL,
    running INT NOT NULL, -- BOOL
    current_step INT NOT NULL,
    current_retry_attempt INT NOT NULL,
    job_state INT NOT NULL)
    
    
    SELECT @job_id = job_id FROM msdb.dbo.sysjobs
    WHERE name = @job_name
    
    SELECT @job_owner = SUSER_SNAME()
    
    INSERT INTO #xp_results
    EXECUTE master.dbo.xp_sqlagent_enum_jobs 1, @job_owner, @job_id
    
    
    -- Start the job if the job is not running
    IF NOT EXISTS(SELECT TOP 1 * FROM #xp_results WHERE running = 1)
    EXEC msdb.dbo.sp_start_job @job_name = @job_name
    
    -- wait a couple of seconds to compute
    WAITFOR DELAY '00:00:02'
    
    DELETE FROM #xp_results
    INSERT INTO #xp_results
    EXECUTE master.dbo.xp_sqlagent_enum_jobs 1, @job_owner, @job_id
    
    WHILE EXISTS(SELECT TOP 1 * FROM #xp_results WHERE running = 1)
    BEGIN
    
    WAITFOR DELAY @WaitTime
    
    DELETE FROM #xp_results
    
    INSERT INTO #xp_results
    EXECUTE master.dbo.xp_sqlagent_enum_jobs 1, @job_owner, @job_id
    
    END
    
    SELECT top 1 @JobCompletionStatus = run_status
    FROM msdb.dbo.sysjobhistory
    WHERE job_id = @job_id
    and step_id = 0
    order by run_date desc, run_time desc
    
    IF @JobCompletionStatus = 1
    PRINT 'The job ran Successful'
    ELSE IF @JobCompletionStatus = 3
    PRINT 'The job is Cancelled'
    ELSE
    BEGIN
    RAISERROR ('[ERROR]:%s job is either failed or not in good state. Please check',16, 1, @job_name) WITH LOG
    END
    
    RETURN @JobCompletionStatus
    
    GO