Viewing SQL Server Jobs and History

In SQL Server 2000, it was cumbersome to allow those who were not members of the sysadmin group the ability to view SQL Agent Jobs and job history through Enterprise Manager.  SQL Server 2005 has made this a lot easier through the use of specialized database roles in the msdb database for viewing such information.  In the scripts below, I get around these SQL 2000 restrictions with a couple of custom objects that allow you to view SQL Agent Job status and history.

First, I need to create a function that formats the job date related data from the SQL Server system tables so that it is in a format that is a bit easier on the eyes.  I am creating this function in the msdb database because this is where the system tables are located.  If you aren't already created backups of your msdb database, now is a really good time to start.

USE [msdb]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[fn_GetJobDate]
(
 @LastRunDate INT,
 @LastRunTime INT
)
RETURNS SMALLDATETIME
AS BEGIN
      DECLARE @Date SMALLDATETIME
      SET @Date = 
        CASE WHEN @LastRunTime > 0
        THEN CAST((SUBSTRING(CAST(@LastRunDate AS VARCHAR(10)),
                    5, 2) + '/'
                    + RIGHT(CAST(@LastRunDate AS VARCHAR(10)), 2)
                    + '/'
                    + LEFT(CAST(@LastRunDate AS VARCHAR(10)), 4)
                    + ' '
                    + CAST(CASE WHEN SUBSTRING(RIGHT(STUFF(' ', 1, 1, '000000') + 
                            CONVERT(VARCHAR(6), @LastRunTime), 6), 1, 2) > 12
                      THEN SUBSTRING(RIGHT(STUFF(' ', 1, 1, '000000') + 
                            CONVERT(VARCHAR(6), @LastRunTime), 6), 1, 2) - 12
                      ELSE SUBSTRING(RIGHT(STUFF(' ', 1, 1, '000000') + 
                            CONVERT(VARCHAR(6), @LastRunTime), 6), 1, 2)
                    END AS VARCHAR(2)) + ':'
                    + SUBSTRING(RIGHT(STUFF(' ', 1, 1, '000000')
                            + convert(varchar(6), @LastRunTime),
                            6), 3, 2) + ':'
                    + SUBSTRING(RIGHT(STUFF(' ', 1, 1, '000000')
                            + convert(varchar(6), @LastRunTime),
                            6), 5, 2)) AS SMALLDATETIME)
        ELSE NULL
        END

      RETURN (@Date)
   END

Now that I have my function created in msdb for formatting the dates, I can create my stored procedure that pulls the job status and history.  This procedure is also created in msdb, and accepts one parameter, the Job Name.  You can provide the name of a specific job and it will give you job-specific details.  If you omit the value for the @JobName parameter, it will return information for all jobs for the SQL Agent. 

USE msdb
GO

CREATE PROCEDURE usp_GetJobInformation
(
    @JobName VARCHAR(255) = NULL
)
AS
BEGIN
    IF OBJECT_ID('tempdb..#Results')>0
        DROP TABLE #Results
   
    CREATE TABLE #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,
        request_source            INT              NOT NULL,
        request_source_id         SYSNAME   COLLATE DATABASE_DEFAULT NULL,
        running                   INT              NOT NULL,
        current_step              INT              NOT NULL,
        current_retry_attempt     INT              NOT NULL,
        job_state                 INT              NOT NULL
    )
   
    DECLARE @JobID VARCHAR(100)
    SELECT TOP 1 @JobID = job_ID FROM msdb.dbo.sysjobs
   
    INSERT INTO #Results
    EXECUTE master.dbo.xp_sqlagent_enum_jobs 1, @JobID
   
    SELECT
        s.Name,
        CASE WHEN s.enabled = 0 THEN 'No' ELSE 'Yes' END AS Enabled,
        CASE WHEN next_run_date > 0 THEN 'Yes' ELSE 'No' END AS Scheduled,
        sc.name AS Category,
        current_step AS CurrentExecutionStep,
        msdb.dbo.fn_GetJobDate(last_run_date,last_run_time) AS LastRunDate,
        msdb.dbo.fn_GetJobDate(next_run_date, next_run_time) AS NextRunDate,
        CASE WHEN xp.running = 0 THEN 'Not Running' ELSE 'Executing...' END AS Status,
        ISNULL((
            SELECT 
                CASE 
                    WHEN run_status = 1 THEN 'Succeeded' 
                    WHEN run_status = 3 THEN 'Cancelled' 
                    WHEN run_status = 0 THEN 'Failed' 
                    WHEN run_status IS NULL THEN 'Unknown' 
                END AS LastRunStatus
            FROM
                msdb..sysjobhistory sho
            WHERE
                sho.job_id = xp.job_id AND
                sho.instance_id =
                (
                    SELECT MAX(instance_id)
                    FROM msdb..sysjobhistory sj (NOLOCK)
                    WHERE sj.job_id = sho.job_id
                )
        ) ,'Unknown') AS LastRunStatus
    FROM     #Results xp    
        INNER JOIN msdb..sysjobs s on xp.job_id = s.job_id
        INNER JOIN msdb..syscategories sc on s.category_id = sc.category_id
    WHERE
        s.Name = ISNULL(@JobName, s.Name)
    ORDER BY s.Name

    BEGIN
    IF OBJECT_ID('tempdb..#JobHistory')>0
        DROP TABLE #JobHistory
   

        CREATE TABLE #JobHistory
        (
            JobName SYSNAME,
            StepID INT,
            StepName SYSNAME,
            Message NVARCHAR(1024),
            RunStatus INT,
            RunDate INT,
            RunTime INT ,
            RunDuration INT    ,
            operator_emailed NVARCHAR(20),
            operator_netsent NVARCHAR(20),
            operator_paged NVARCHAR(20)               
        )

        INSERT INTO #JobHistory
        SELECT
            sjj.Name,
            sjh.step_id,
            sjh.step_name,
            sjh.message,
            sjh.run_status,
            sjh.run_date,
            sjh.run_time,
            sjh.run_duration,
            operator_emailed = so1.name,
            operator_netsent = so2.name,
            operator_paged = so3.name
        FROM
            msdb.dbo.sysjobhistory                sjh
            JOIN msdb.dbo.sysjobs sjj ON sjh.job_id = sjj.job_id
            LEFT OUTER JOIN msdb.dbo.sysoperators so1  ON (sjh.operator_id_emailed = so1.id)
            LEFT OUTER JOIN msdb.dbo.sysoperators so2  ON (sjh.operator_id_netsent = so2.id)
            LEFT OUTER JOIN msdb.dbo.sysoperators so3  ON (sjh.operator_id_paged = so3.id),
            msdb.dbo.sysjobs                 sj
        WHERE
        (sj.job_id = sjh.job_id)

        SELECT
            JobName, StepID, StepName, 
            Message, 
            msdb.dbo.fn_GetJobDate(RunDate, RunTime) AS LastRunTime,
            CASE RunStatus
                WHEN 0 THEN 'Failed'
                WHEN 1 THEN 'Succeeded'
                WHEN 2 THEN 'Retry (step only)'
                WHEN 3 THEN 'Canceled'
                WHEN 4 THEN 'In-progress message'
                WHEN 5 THEN 'Unknown'
            END AS RunStatus
        FROM #JobHistory
        ORDER BY JobName, LastRunTime DESC, StepID ASC
    END

END


GO

In terms of security for the above stored procedure, you'll need to add any user or group for which you want to run the procedure as a User in the msdb database.  You'll also need to give this user EXECUTE permissions on the stored procedure. 


Posted 2/25/2009 2:05 PM by TimChapman
Copyright SQL Server Nation 2010
Powered by Community Server (Non-Commercial Edition), by Telligent Systems