大风车01

归来仍少年

创建SQL SERVER作业失败记录的存储过程

IF NOT EXISTS (SELECT * FROM sys.tables WHERE tables.name = 'sql_server_agent_job')
BEGIN
    CREATE TABLE dbo.sql_server_agent_job
    (    sql_server_agent_job_id INT NOT NULL IDENTITY(1,1) CONSTRAINT PK_sql_server_agent_job PRIMARY KEY CLUSTERED,
        sql_server_agent_job_id_guid UNIQUEIDENTIFIER NOT NULL,
        sql_server_agent_job_name NVARCHAR(128) NOT NULL,
        job_create_datetime_utc DATETIME NOT NULL, 
        job_last_modified_datetime_utc DATETIME NOT NULL,
        is_enabled BIT NOT NULL,
        is_deleted BIT NOT NULL,
        job_category_name VARCHAR(100) NOT NULL);
END
GO
 
IF NOT EXISTS (SELECT * FROM sys.tables WHERE tables.name = 'sql_server_agent_job_failure')
BEGIN
    CREATE TABLE dbo.sql_server_agent_job_failure
    (    sql_server_agent_job_failure_id INT NOT NULL IDENTITY(1,1) CONSTRAINT PK_sql_server_agent_job_failure PRIMARY KEY CLUSTERED,
        sql_server_agent_job_id INT NOT NULL CONSTRAINT FK_sql_server_agent_job_failure_sql_server_agent_job FOREIGN KEY REFERENCES dbo.sql_server_agent_job (sql_server_agent_job_id),
        sql_server_agent_instance_id INT NOT NULL,
        job_start_time_utc DATETIME NOT NULL,
        job_failure_time_utc DATETIME NOT NULL,
        job_failure_step_number SMALLINT NOT NULL,
        job_failure_step_name VARCHAR(250) NOT NULL,
        job_failure_message VARCHAR(MAX) NOT NULL,
        job_step_failure_message VARCHAR(MAX) NOT NULL,
        job_step_severity INT NOT NULL,
        job_step_message_id INT NOT NULL,
        retries_attempted INT NOT NULL,
        has_email_been_sent_to_operator BIT NOT NULL);
 
    CREATE NONCLUSTERED INDEX NCI_sql_server_agent_job_failure_sql_server_agent_job_id ON dbo.sql_server_agent_job_failure (sql_server_agent_job_id);
    CREATE NONCLUSTERED INDEX NCI_sql_server_agent_job_failure_sql_server_agent_instance_id ON dbo.sql_server_agent_job_failure (sql_server_agent_instance_id);
END
GO
 
 
 
 
 
 
 
 
 
 
CREATE    PROCEDURE [dbo].[monitor_job_failures_forReportingService]
    
AS
BEGIN
    SET NOCOUNT ON;
    declare @minutes_to_monitor SMALLINT = 1440
 
    DECLARE @utc_offset INT;
    SELECT
        @utc_offset = -1 * DATEDIFF(HOUR, GETUTCDATE(), GETDATE());
        
    -- First, collect list of SQL Server agent jobs and update ours as needed.
    -- Update our jobs data with any changes since the last update time.
    MERGE INTO dbo.sql_server_agent_job AS TARGET
        USING (SELECT
                    sysjobs.job_id AS sql_server_agent_job_id_guid,
                    sysjobs.name AS sql_server_agent_job_name,
                    sysjobs.date_created AS job_create_datetime_utc,
                    sysjobs.date_modified AS job_last_modified_datetime_utc,
                    sysjobs.enabled AS is_enabled,
                    0 AS is_deleted,
                    ISNULL(syscategories.name, '') AS job_category_name
               FROM msdb.dbo.sysjobs
               LEFT JOIN msdb.dbo.syscategories
               ON syscategories.category_id = sysjobs.category_id) AS SOURCE
        ON (SOURCE.sql_server_agent_job_id_guid = TARGET.sql_server_agent_job_id_guid)
        WHEN NOT MATCHED BY TARGET
            THEN INSERT
                (sql_server_agent_job_id_guid, sql_server_agent_job_name, job_create_datetime_utc, job_last_modified_datetime_utc,
                 is_enabled, is_deleted, job_category_name)
            VALUES    (
                SOURCE.sql_server_agent_job_id_guid,
                SOURCE.sql_server_agent_job_name,
                SOURCE.job_create_datetime_utc,
                SOURCE.job_last_modified_datetime_utc,
                SOURCE.is_enabled,
                SOURCE.is_deleted,
                SOURCE.job_category_name)
        WHEN MATCHED AND SOURCE.job_last_modified_datetime_utc > TARGET.job_last_modified_datetime_utc
            THEN UPDATE
                SET sql_server_agent_job_name = SOURCE.sql_server_agent_job_name,
                    job_create_datetime_utc = SOURCE.job_create_datetime_utc,
                    job_last_modified_datetime_utc = SOURCE.job_last_modified_datetime_utc,
                    is_enabled = SOURCE.is_enabled,
                    is_deleted = SOURCE.is_deleted,
                    job_category_name = SOURCE.job_category_name;
    -- If a job was deleted, then mark it as no longer enabled.
    UPDATE sql_server_agent_job
        SET is_enabled = 0,
            is_deleted = 1
    FROM dbo.sql_server_agent_job
    LEFT JOIN msdb.dbo.sysjobs
    ON sysjobs.job_id = sql_server_agent_job.sql_server_agent_job_id_guid
    WHERE sysjobs.job_id IS NULL;
    -- Find all recent job failures and log them in the target log table.
    WITH CTE_NORMALIZE_DATETIME_DATA AS (
        SELECT
            sysjobhistory.job_id AS sql_server_agent_job_id_guid,
            CAST(sysjobhistory.run_date AS VARCHAR(MAX)) AS run_date_string, 
            REPLICATE('0', 6 - LEN(CAST(sysjobhistory.run_time AS VARCHAR(MAX)))) + CAST(sysjobhistory.run_time AS VARCHAR(MAX)) AS run_time_string,
            REPLICATE('0', 6 - LEN(CAST(sysjobhistory.run_duration AS VARCHAR(MAX)))) + CAST(sysjobhistory.run_duration AS VARCHAR(MAX)) AS run_duration_string,
            sysjobhistory.run_status,
            sysjobhistory.message,
            sysjobhistory.instance_id
        FROM msdb.dbo.sysjobhistory WITH (NOLOCK)
        WHERE sysjobhistory.run_status = 0
        AND sysjobhistory.step_id = 0),
    CTE_GENERATE_DATETIME_DATA AS (
        SELECT
            CTE_NORMALIZE_DATETIME_DATA.sql_server_agent_job_id_guid,
            CAST(SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_date_string, 5, 2) + '/' + SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_date_string, 7, 2) + '/' + SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_date_string, 1, 4) AS DATETIME) +
            CAST(STUFF(STUFF(CTE_NORMALIZE_DATETIME_DATA.run_time_string, 5, 0, ':'), 3, 0, ':') AS DATETIME) AS job_start_datetime,
            CAST(SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_duration_string, 1, 2) AS INT) * 3600 +
                CAST(SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_duration_string, 3, 2) AS INT) * 60 + 
                CAST(SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_duration_string, 5, 2) AS INT) AS job_duration_seconds,
            CASE CTE_NORMALIZE_DATETIME_DATA.run_status
                WHEN 0 THEN 'Failure'
                WHEN 1 THEN 'Success'
                WHEN 2 THEN 'Retry'
                WHEN 3 THEN 'Canceled'
                ELSE 'Unknown'
            END AS job_status,
            CTE_NORMALIZE_DATETIME_DATA.message,
            CTE_NORMALIZE_DATETIME_DATA.instance_id
        FROM CTE_NORMALIZE_DATETIME_DATA)
    SELECT
        CTE_GENERATE_DATETIME_DATA.sql_server_agent_job_id_guid,
        DATEADD(HOUR, @utc_offset, CTE_GENERATE_DATETIME_DATA.job_start_datetime) AS job_start_time_utc,
        DATEADD(HOUR, @utc_offset, DATEADD(SECOND, ISNULL(CTE_GENERATE_DATETIME_DATA.job_duration_seconds, 0), CTE_GENERATE_DATETIME_DATA.job_start_datetime)) AS job_failure_time_utc,
        ISNULL(CTE_GENERATE_DATETIME_DATA.message, '') AS job_failure_message,
        CTE_GENERATE_DATETIME_DATA.instance_id
    INTO #job_failure
    FROM CTE_GENERATE_DATETIME_DATA
    WHERE DATEADD(HOUR, @utc_offset, CTE_GENERATE_DATETIME_DATA.job_start_datetime) > DATEADD(MINUTE, -1 * @minutes_to_monitor, GETUTCDATE());
 
    WITH CTE_NORMALIZE_DATETIME_DATA AS (
        SELECT
            sysjobhistory.job_id AS sql_server_agent_job_id_guid,
            CAST(sysjobhistory.run_date AS VARCHAR(MAX)) AS run_date_string, 
            REPLICATE('0', 6 - LEN(CAST(sysjobhistory.run_time AS VARCHAR(MAX)))) + CAST(sysjobhistory.run_time AS VARCHAR(MAX)) AS run_time_string,
            REPLICATE('0', 6 - LEN(CAST(sysjobhistory.run_duration AS VARCHAR(MAX)))) + CAST(sysjobhistory.run_duration AS VARCHAR(MAX)) AS run_duration_string,
            sysjobhistory.run_status,
            sysjobhistory.step_id,
            sysjobhistory.step_name,
            sysjobhistory.message,
            sysjobhistory.retries_attempted,
            sysjobhistory.sql_severity,
            sysjobhistory.sql_message_id,
            sysjobhistory.instance_id
        FROM msdb.dbo.sysjobhistory WITH (NOLOCK)
        WHERE sysjobhistory.run_status = 0
        AND sysjobhistory.step_id > 0),
    CTE_GENERATE_DATETIME_DATA AS (
        SELECT
            CTE_NORMALIZE_DATETIME_DATA.sql_server_agent_job_id_guid,
            CAST(SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_date_string, 5, 2) + '/' + SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_date_string, 7, 2) + '/' + SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_date_string, 1, 4) AS DATETIME) +
            CAST(STUFF(STUFF(CTE_NORMALIZE_DATETIME_DATA.run_time_string, 5, 0, ':'), 3, 0, ':') AS DATETIME) AS job_start_datetime,
            CAST(SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_duration_string, 1, 2) AS INT) * 3600 +
                CAST(SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_duration_string, 3, 2) AS INT) * 60 + 
                CAST(SUBSTRING(CTE_NORMALIZE_DATETIME_DATA.run_duration_string, 5, 2) AS INT) AS job_duration_seconds,
            CASE CTE_NORMALIZE_DATETIME_DATA.run_status
                WHEN 0 THEN 'Failure'
                WHEN 1 THEN 'Success'
                WHEN 2 THEN 'Retry'
                WHEN 3 THEN 'Canceled'
                ELSE 'Unknown'
            END AS job_status,
            CTE_NORMALIZE_DATETIME_DATA.step_id,
            CTE_NORMALIZE_DATETIME_DATA.step_name,
            CTE_NORMALIZE_DATETIME_DATA.message,
            CTE_NORMALIZE_DATETIME_DATA.retries_attempted,
            CTE_NORMALIZE_DATETIME_DATA.sql_severity,
            CTE_NORMALIZE_DATETIME_DATA.sql_message_id,
            CTE_NORMALIZE_DATETIME_DATA.instance_id
        FROM CTE_NORMALIZE_DATETIME_DATA)
    SELECT
        CTE_GENERATE_DATETIME_DATA.sql_server_agent_job_id_guid,
        DATEADD(HOUR, @utc_offset, CTE_GENERATE_DATETIME_DATA.job_start_datetime) AS job_start_time_utc,
        DATEADD(HOUR, @utc_offset, DATEADD(SECOND, ISNULL(CTE_GENERATE_DATETIME_DATA.job_duration_seconds, 0), CTE_GENERATE_DATETIME_DATA.job_start_datetime)) AS job_failure_time_utc,
        CTE_GENERATE_DATETIME_DATA.step_id AS job_failure_step_number,
        ISNULL(CTE_GENERATE_DATETIME_DATA.message, '') AS job_step_failure_message,
        CTE_GENERATE_DATETIME_DATA.sql_severity AS job_step_severity,
        CTE_GENERATE_DATETIME_DATA.retries_attempted,
        CTE_GENERATE_DATETIME_DATA.step_name,
        CTE_GENERATE_DATETIME_DATA.sql_message_id,
        CTE_GENERATE_DATETIME_DATA.instance_id
    INTO #job_step_failure
    FROM CTE_GENERATE_DATETIME_DATA
    WHERE DATEADD(HOUR, @utc_offset, CTE_GENERATE_DATETIME_DATA.job_start_datetime) > DATEADD(MINUTE, -1 * @minutes_to_monitor, GETUTCDATE());
 
    -- Get jobs that failed due to failed steps.
    WITH CTE_FAILURE_STEP AS (
        SELECT
            *,
            ROW_NUMBER() OVER (PARTITION BY job_step_failure.sql_server_agent_job_id_guid, job_step_failure.job_failure_time_utc ORDER BY job_step_failure.job_failure_step_number DESC) AS recent_step_rank
        FROM #job_step_failure job_step_failure)
    INSERT INTO dbo.sql_server_agent_job_failure
        (sql_server_agent_job_id, sql_server_agent_instance_id, job_start_time_utc, job_failure_time_utc, job_failure_step_number, job_failure_step_name,
         job_failure_message, job_step_failure_message, job_step_severity, job_step_message_id, retries_attempted, has_email_been_sent_to_operator)
    SELECT
        sql_server_agent_job.sql_server_agent_job_id,
        CTE_FAILURE_STEP.instance_id,
        job_failure.job_start_time_utc,
        CTE_FAILURE_STEP.job_failure_time_utc,
        CTE_FAILURE_STEP.job_failure_step_number,
        CTE_FAILURE_STEP.step_name AS job_failure_step_name,
        job_failure.job_failure_message,
        CTE_FAILURE_STEP.job_step_failure_message,
        CTE_FAILURE_STEP.job_step_severity,
        CTE_FAILURE_STEP.sql_message_id AS job_step_message_id,
        CTE_FAILURE_STEP.retries_attempted,
        0 AS has_email_been_sent_to_operator
    FROM #job_failure job_failure
    INNER JOIN dbo.sql_server_agent_job
    ON job_failure.sql_server_agent_job_id_guid = sql_server_agent_job.sql_server_agent_job_id_guid
    INNER JOIN CTE_FAILURE_STEP
    ON job_failure.sql_server_agent_job_id_guid = CTE_FAILURE_STEP.sql_server_agent_job_id_guid
    AND job_failure.job_failure_time_utc = CTE_FAILURE_STEP.job_failure_time_utc
    WHERE CTE_FAILURE_STEP.recent_step_rank = 1
    AND CTE_FAILURE_STEP.instance_id NOT IN (SELECT sql_server_agent_job_failure.sql_server_agent_instance_id FROM dbo.sql_server_agent_job_failure);
    -- Get jobs that failed without any failed steps.
    INSERT INTO dbo.sql_server_agent_job_failure
        (sql_server_agent_job_id, sql_server_agent_instance_id, job_start_time_utc, job_failure_time_utc, job_failure_step_number, job_failure_step_name,
         job_failure_message, job_step_failure_message, job_step_severity, job_step_message_id, retries_attempted, has_email_been_sent_to_operator)
    SELECT
        sql_server_agent_job.sql_server_agent_job_id,
        job_failure.instance_id,
        job_failure.job_start_time_utc,
        job_failure.job_failure_time_utc,
        0 AS job_failure_step_number,
        '' AS job_failure_step_name,
        job_failure.job_failure_message,
        '' AS job_step_failure_message,
        -1 AS job_step_severity,
        -1 AS job_step_message_id,
        0 AS retries_attempted,
        0 AS has_email_been_sent_to_operator
    FROM #job_failure job_failure
    INNER JOIN dbo.sql_server_agent_job
    ON job_failure.sql_server_agent_job_id_guid = sql_server_agent_job.sql_server_agent_job_id_guid
    WHERE job_failure.instance_id NOT IN (SELECT sql_server_agent_job_failure.sql_server_agent_instance_id FROM dbo.sql_server_agent_job_failure)
    AND NOT EXISTS (SELECT * FROM #job_step_failure job_step_failure WHERE job_failure.sql_server_agent_job_id_guid = job_step_failure.sql_server_agent_job_id_guid    AND job_failure.job_failure_time_utc = job_step_failure.job_failure_time_utc);
    
    -- Get job steps that failed, but for jobs that succeeded.
    WITH CTE_FAILURE_STEP AS (
        SELECT
            *,
            ROW_NUMBER() OVER (PARTITION BY job_step_failure.sql_server_agent_job_id_guid, job_step_failure.job_failure_time_utc ORDER BY job_step_failure.job_failure_step_number DESC) AS recent_step_rank
        FROM #job_step_failure job_step_failure)
    INSERT INTO dbo.sql_server_agent_job_failure
        (sql_server_agent_job_id, sql_server_agent_instance_id, job_start_time_utc, job_failure_time_utc, job_failure_step_number, job_failure_step_name,
         job_failure_message, job_step_failure_message, job_step_severity, job_step_message_id, retries_attempted, has_email_been_sent_to_operator)
    SELECT
        sql_server_agent_job.sql_server_agent_job_id,
        CTE_FAILURE_STEP.instance_id,
        CTE_FAILURE_STEP.job_start_time_utc,
        CTE_FAILURE_STEP.job_failure_time_utc,
        CTE_FAILURE_STEP.job_failure_step_number,
        CTE_FAILURE_STEP.step_name AS job_failure_step_name,
        '' AS job_failure_message,
        CTE_FAILURE_STEP.job_step_failure_message,
        CTE_FAILURE_STEP.job_step_severity,
        CTE_FAILURE_STEP.sql_message_id AS job_step_message_id,
        CTE_FAILURE_STEP.retries_attempted,
        0 AS has_email_been_sent_to_operator
    FROM CTE_FAILURE_STEP
    INNER JOIN dbo.sql_server_agent_job
    ON CTE_FAILURE_STEP.sql_server_agent_job_id_guid = sql_server_agent_job.sql_server_agent_job_id_guid
    LEFT JOIN #job_failure job_failure
    ON job_failure.sql_server_agent_job_id_guid = CTE_FAILURE_STEP.sql_server_agent_job_id_guid
    AND job_failure.job_failure_time_utc = CTE_FAILURE_STEP.job_failure_time_utc
    WHERE CTE_FAILURE_STEP.recent_step_rank = 1
    AND job_failure.sql_server_agent_job_id_guid IS NULL
    AND CTE_FAILURE_STEP.instance_id NOT IN (SELECT sql_server_agent_job_failure.sql_server_agent_instance_id FROM dbo.sql_server_agent_job_failure);
 
 
        ---This part of query modified for Reporting Service---
 
        SELECT sql_server_agent_job_name,job_failure_message,job_step_failure_message,job_failure_step_name
        FROM dbo.sql_server_agent_job_failure
        INNER JOIN dbo.sql_server_agent_job
        ON sql_server_agent_job.sql_server_agent_job_id = sql_server_agent_job_failure.sql_server_agent_job_id
        WHERE sql_server_agent_job_failure.has_email_been_sent_to_operator = 0
        ORDER BY sql_server_agent_job_failure.job_failure_time_utc ASC
    
         
        
 
        UPDATE sql_server_agent_job_failure
            SET has_email_been_sent_to_operator = 1
        FROM dbo.sql_server_agent_job_failure
        WHERE sql_server_agent_job_failure.has_email_been_sent_to_operator = 0;
        -- This part of query modified for reporting service---
    IF @@ROWCOUNT=0
    BEGIN
    RAISERROR ('No Records Found',16,1)
    END
    ---******************************************************------
    DROP TABLE #job_step_failure;
    DROP TABLE #job_failure;
 
    
END



EXEC monitor_job_failures_forReportingService

 

posted on 2022-07-20 10:11  大风车01  阅读(95)  评论(0编辑  收藏  举报

导航