Script for verifying currently executing jobs?

  • We have a need to develop a script or SP that returns the status (executing or the results of last run) of specific jobs are (or should be) currently executing.  I've found scripts here that return the historical results of jobs, but they do not return anything if the job is currently executing and has no prior history.  Any ideas?

  • I had the same problem and I played around with some MS code and came up with this stored proc, hope it's what you're looking for:

    CREATE PROCEDURE p_mct_running_agent_job_info

    @job_name varchar(200),

    @is_runningBIT OUTPUT--1 means in some running state, 0 means idle

    AS

    BEGIN

    DECLARE @is_sysadmin INT

    DECLARE @job_owner sysname

    SET NOCOUNT ON

    declare @execution_status INT

    set @execution_status = 0 --0 means not idle or suspended

    --get the job id of the agent job

    declare @job_id uniqueidentifier

    set @job_id = (select job_id from msdb..sysjobs where name=@job_name)

    -- By 'composite' we mean a combination of sysjobs and xp_sqlagent_enum_jobs data.

    -- This proc should only ever be called by sp_help_job, so we don't verify the

    -- parameters (sp_help_job has already done this).

    -- Step 1: Create intermediate work tables

    CREATE TABLE #job_execution_state (job_id UNIQUEIDENTIFIER NOT NULL,

    date_started INT NOT NULL,

    time_started INT NOT NULL,

    execution_job_status INT NOT NULL,

    execution_step_id INT NULL,

    execution_step_name sysname COLLATE database_default NULL,

    execution_retry_attempt INT NOT NULL,

    next_run_date INT NOT NULL,

    next_run_time INT NOT NULL,

    next_run_schedule_id INT NOT NULL)

    CREATE TABLE #filtered_jobs (job_id UNIQUEIDENTIFIER NOT NULL,

    date_created DATETIME NOT NULL,

    date_last_modified DATETIME NOT NULL,

    current_execution_status INT NULL,

    current_execution_step sysname COLLATE database_default NULL,

    current_retry_attempt INT NULL,

    last_run_date INT NOT NULL,

    last_run_time INT NOT NULL,

    last_run_outcome INT NOT NULL,

    next_run_date INT NULL,

    next_run_time INT NULL,

    next_run_schedule_id INT NULL,

    type INT NOT NULL)

    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)

    -- Step 2: Capture job execution information (for local jobs only since that's all SQLServerAgent caches)

    SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0)

    SELECT @job_owner = SUSER_SNAME()

    IF ((@@microsoftversion / 0x01000000) >= 8) -- SQL Server 8.0 or greater

    INSERT INTO #xp_results

    EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, @job_owner, @job_id

    ELSE

    INSERT INTO #xp_results

    EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, @job_owner

    INSERT INTO #job_execution_state

    SELECT xpr.job_id,

    xpr.last_run_date,

    xpr.last_run_time,

    xpr.job_state,

    sjs.step_id,

    sjs.step_name,

    xpr.current_retry_attempt,

    xpr.next_run_date,

    xpr.next_run_time,

    xpr.next_run_schedule_id

    FROM #xp_results xpr

    LEFT OUTER JOIN msdb.dbo.sysjobsteps sjs ON ((xpr.job_id = sjs.job_id) AND (xpr.current_step = sjs.step_id)),

    msdb.dbo.sysjobs_view sjv

    WHERE (sjv.job_id = xpr.job_id)

    -- Step 3: Filter on everything but dates and job_type

    IF ((@execution_status IS NULL) AND

    (@job_id IS NULL))

    BEGIN

    -- Optimize for the frequently used case...

    INSERT INTO #filtered_jobs

    SELECT sjv.job_id,

    sjv.date_created,

    sjv.date_modified,

    ISNULL(jes.execution_job_status, 4), -- Will be NULL if the job is non-local or is not in #job_execution_state (NOTE: 4 = STATE_IDLE)

    CASE ISNULL(jes.execution_step_id, 0)

    WHEN 0 THEN NULL -- Will be NULL if the job is non-local or is not in #job_execution_state

    ELSE CONVERT(NVARCHAR, jes.execution_step_id) + N' (' + jes.execution_step_name + N')'

    END,

    jes.execution_retry_attempt, -- Will be NULL if the job is non-local or is not in #job_execution_state

    0, -- last_run_date placeholder (we'll fix it up in step 3.3)

    0, -- last_run_time placeholder (we'll fix it up in step 3.3)

    5, -- last_run_outcome placeholder (we'll fix it up in step 3.3 - NOTE: We use 5 just in case there are no jobservers for the job)

    jes.next_run_date, -- Will be NULL if the job is non-local or is not in #job_execution_state

    jes.next_run_time, -- Will be NULL if the job is non-local or is not in #job_execution_state

    jes.next_run_schedule_id, -- Will be NULL if the job is non-local or is not in #job_execution_state

    0 -- type placeholder (we'll fix it up in step 3.4)

    FROM msdb.dbo.sysjobs_view sjv

    LEFT OUTER JOIN #job_execution_state jes ON (sjv.job_id = jes.job_id)

    END

    ELSE

    BEGIN

    INSERT INTO #filtered_jobs

    SELECT DISTINCT

    sjv.job_id,

    sjv.date_created,

    sjv.date_modified,

    ISNULL(jes.execution_job_status, 4), -- Will be NULL if the job is non-local or is not in #job_execution_state (NOTE: 4 = STATE_IDLE)

    CASE ISNULL(jes.execution_step_id, 0)

    WHEN 0 THEN NULL -- Will be NULL if the job is non-local or is not in #job_execution_state

    ELSE CONVERT(NVARCHAR, jes.execution_step_id) + N' (' + jes.execution_step_name + N')'

    END,

    jes.execution_retry_attempt, -- Will be NULL if the job is non-local or is not in #job_execution_state

    0, -- last_run_date placeholder (we'll fix it up in step 3.3)

    0, -- last_run_time placeholder (we'll fix it up in step 3.3)

    5, -- last_run_outcome placeholder (we'll fix it up in step 3.3 - NOTE: We use 5 just in case there are no jobservers for the job)

    jes.next_run_date, -- Will be NULL if the job is non-local or is not in #job_execution_state

    jes.next_run_time, -- Will be NULL if the job is non-local or is not in #job_execution_state

    jes.next_run_schedule_id, -- Will be NULL if the job is non-local or is not in #job_execution_state

    0 -- type placeholder (we'll fix it up in step 3.4)

    FROM msdb.dbo.sysjobs_view sjv

    LEFT OUTER JOIN #job_execution_state jes ON (sjv.job_id = jes.job_id)

    LEFT OUTER JOIN msdb.dbo.sysjobsteps sjs ON (sjv.job_id = sjs.job_id)

    WHERE (

    ((@execution_status IS NULL) OR ((@execution_status > 0) AND (jes.execution_job_status = @execution_status))

    OR ((@execution_status = 0) AND (jes.execution_job_status 4) AND (jes.execution_job_status 5)))

    AND ((@job_id IS NULL) OR (sjv.job_id = @job_id))

    )

    END

    -- Step 3.1: Change the execution status of non-local jobs from 'Idle' to 'Unknown'

    UPDATE #filtered_jobs

    SET current_execution_status = NULL

    WHERE (current_execution_status = 4)

    AND (job_id IN (SELECT job_id

    FROM msdb.dbo.sysjobservers

    WHERE (server_id 0)))

    -- Step 3.2: Check that if the user asked to see idle jobs that we still have some.

    -- If we don't have any then the query should return no rows.

    IF (@execution_status = 4) AND

    (NOT EXISTS (SELECT *

    FROM #filtered_jobs

    WHERE (current_execution_status = 4)))

    BEGIN

    TRUNCATE TABLE #filtered_jobs

    END

    -- Step 3.3: Populate the last run date/time/outcome [this is a little tricky since for

    -- multi-server jobs there are multiple last run details in sysjobservers, so

    -- we simply choose the most recent].

    IF (EXISTS (SELECT *

    FROM msdb.dbo.systargetservers))

    BEGIN

    UPDATE #filtered_jobs

    SET last_run_date = sjs.last_run_date,

    last_run_time = sjs.last_run_time,

    last_run_outcome = sjs.last_run_outcome

    FROM #filtered_jobs fj,

    msdb.dbo.sysjobservers sjs

    WHERE (CONVERT(FLOAT, sjs.last_run_date) * 1000000) + sjs.last_run_time =

    (SELECT MAX((CONVERT(FLOAT, last_run_date) * 1000000) + last_run_time)

    FROM msdb.dbo.sysjobservers

    WHERE (job_id = sjs.job_id))

    AND (fj.job_id = sjs.job_id)

    END

    ELSE

    BEGIN

    UPDATE #filtered_jobs

    SET last_run_date = sjs.last_run_date,

    last_run_time = sjs.last_run_time,

    last_run_outcome = sjs.last_run_outcome

    FROM #filtered_jobs fj,

    msdb.dbo.sysjobservers sjs

    WHERE (fj.job_id = sjs.job_id)

    END

    -- Step 3.4 : Set the type of the job to local (1) or multi-server (2)

    -- NOTE: If the job has no jobservers then it wil have a type of 0 meaning

    -- unknown. This is marginally inconsistent with the behaviour of

    -- defaulting the category of a new job to [Uncategorized (Local)], but

    -- prevents incompletely defined jobs from erroneously showing up as valid

    -- local jobs.

    UPDATE #filtered_jobs

    SET type = 1 -- LOCAL

    FROM #filtered_jobs fj,

    msdb.dbo.sysjobservers sjs

    WHERE (fj.job_id = sjs.job_id)

    AND (server_id = 0)

    UPDATE #filtered_jobs

    SET type = 2 -- MULTI-SERVER

    FROM #filtered_jobs fj,

    msdb.dbo.sysjobservers sjs

    WHERE (fj.job_id = sjs.job_id)

    AND (server_id 0)

    -- Return the result set (NOTE: No filtering occurs here)

    DECLARE @record_count int

    SET @record_count =

    (

    SELECT count(*)

    FROM #filtered_jobs fj

    LEFT OUTER JOIN msdb.dbo.sysjobs_view sjv ON (fj.job_id = sjv.job_id)

    LEFT OUTER JOIN msdb.dbo.sysoperators so1 ON (sjv.notify_email_operator_id = so1.id)

    LEFT OUTER JOIN msdb.dbo.sysoperators so2 ON (sjv.notify_netsend_operator_id = so2.id)

    LEFT OUTER JOIN msdb.dbo.sysoperators so3 ON (sjv.notify_page_operator_id = so3.id)

    LEFT OUTER JOIN msdb.dbo.syscategories sc ON (sjv.category_id = sc.category_id)

    )

    IF (@record_count >0)

    BEGIN

    SET @is_running = 1

    END

    ELSE

    BEGIN

    SET @is_running = 0

    END

    -- Clean up

    DROP TABLE #job_execution_state

    DROP TABLE #filtered_jobs

    DROP TABLE #xp_results

    END

    GO

  • Thanks!  We'll give this a try.

  • I use this query to find jobs that have been running for more than an hour:

    select RTrim(J.Name),last_batch

    from msdb..sysjobs J with (nolock)

           inner join master..sysprocesses RP with (nolock)

                    on RP.program_name like ''SQLAgent - TSQL JobStep (Job '' +    

                    master.dbo.fn_varbintohexstr(convert(binary(16),J.job_id )) + ''%''

    where datediff(hour,last_batch,getdate()) > 1

    I put this in a step of a job that runs every morning so I can see night jobs that are still running.

    Greg

    Greg

  • Thanks, Greg.  I've passed it on to the developer.

Viewing 5 posts - 1 through 4 (of 4 total)

You must be logged in to reply to this topic. Login to reply