在 sysjobhistory 中将单次运行的作业步骤链接在一起

SQL*_*oug 4 sql-server

我们有几个运行 SQL 代理作业的 SQL Server,在它们自己的 sysjobhistory 表中记录历史记录。我正在尝试设置一个集中式服务器,其中包含一项作业,该作业从所有其他服务器收集历史记录,对其进行一些格式化,然后将其放入名为 AllJobHistory 的表中。作为此过程的一部分,我希望用一列来指示作业的多个步骤是同一作业运行的一部分。它们已通过 job_id 列标记为同一作业的一部分,但我想知道特定行来自作业的 3:00 运行与 4:00 运行。能够基于此列进行过滤将使我们的故障排除工作变得更加容易,但我没有在任何现有系统表或 DMV 中看到任何将这些步骤链接在一起的内容,是吗?

\n\n

我自己的第一次尝试是使用 run_date、run_time 和 run_duration 列。对于每个步骤,如果我从 run_time 中减去到目前为止的总 run_duration,它应该使我回到与该作业的所有其他运行相比唯一的时间。看起来它一直在工作,直到我发现它不是 \xe2\x80\x99t 的情况(可能是因为 SQL Server 以秒的精度舍入 run_time 和 run_duration )。这是我对查询的尝试(删除了额外的列)。

\n\n
WITH JobDetails AS \n(\n    SELECT \n        QUOTENAME(UPPER(\'ServerName\')) AS [Server],\n        j.job_id AS [JobID],\n        j.name AS [JobName],\n        s.step_id AS [Step],\n        msdb.dbo.agent_datetime(run_date, run_time) AS [RunDate],\n        (run_duration/10000*3600 + (run_duration/100)%100*60 + run_duration%100) AS [RunDurationSeconds]\n    FROM msdb.dbo.sysjobhistory h\n    INNER JOIN msdb.dbo.sysjobs j ON j.job_id = h.job_id\n    LEFT OUTER JOIN msdb.dbo.sysjobsteps s ON s.job_id = h.job_id AND s.step_id = h.step_id \n    WHERE h.step_id != 0\n), GroupedDetails AS (\n    SELECT \n        jd.[Server],\n        jd.[JobID],\n        jd.JobName,\n        jd.Step,\n        jd.RunDate,\n        jd.RunDurationSeconds,\n        DATEADD(SECOND, \n            -ISNULL(SUM(jd.RunDurationSeconds) OVER\n                (PARTITION BY jd.JobName ORDER BY jd.JobName, jd.RunDate, jd.Step \n                ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0), \n            jd.RunDate) AS grp\n    FROM JobDetails AS  jd\n)\nSELECT \n    gd.[Server],\n    gd.JobName,\n    gd.Step,\n    gd.RunDate,\n    gd.RunDurationSeconds,\n    CONVERT(VARCHAR(36), gd.JobID) + \'_\' + FORMAT(gd.grp, \'yyyyMMdd_HHmmss\') AS JobRunString\nFROM GroupedDetails AS gd;\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是一个示例,它按照我的预期工作,对于一个包含三个步骤的工作。请注意,JobRunString 与第一次运行匹配,并与第二次运行匹配。\n工作示例

\n\n

这是一个例子,它没有按照我想要的方式工作。请注意,Step1RunDate + Step1RunDurationSeconds != Step2RunDate,导致 JobRunString 不匹配。\n不工作示例

\n\n

那么,有没有可靠的方法可以将 sysjobhistory 中运行的作业步骤链接在一起?

\n

RDF*_*ozz 5

请注意,sysjobhistory有一个 ID 列 ( instance_id)。对于已完成的作业的每个步骤,表中应至少有一个条目,后跟step_id= 0 的条目,记录作业的结果。每个步骤还记录该步骤的启动时间 (run_daterun_time),该时间等于或大于作业的启动时间。因此,step_id给定运行的 = 0 行的运行时间高于 instance_id相关步骤,但运行时间较低(或相等)

因此,尝试将数据从 where step_id= 0 的行初始提取到临时表(或等效表)中。然后,sysjobhistory具有相同job_id较低 instance_id较高或相等开始时间(从run_daterun_time)的所有行都应该属于您要查找的作业运行。

我在前雇主的一份失败的工作报告中使用了类似的东西。

这是该代码的精简、修改版本。刚才我在 SQL Server 2016 机器上进行了快速测试,它运行正常。但是,我没有任何运行频率足够高的作业,以至于多次运行具有相同的运行时间。

USE msdb;

DECLARE @start_date varchar(8) = '20171001';

IF(OBJECT_ID('tempdb..#failhist') IS NOT NULL) DROP TABLE #failhist;

CREATE TABLE #failhist
(
    [job_name] [sysname] NOT NULL,
    [run_datetime] [datetime] NULL,
    [run_ended] [datetime] NULL,
    [instance_id] [int] NOT NULL,
    [job_id] [uniqueidentifier] NOT NULL,
    [run_date] [int] NOT NULL,
    [run_time] [int] NOT NULL
);


INSERT INTO #failhist
select *
  FROM
(SELECT j.name as job_name
       ,CONVERT(DATETIME, RTRIM(run_date)) + (run_time * 9 + run_time % 10000 * 6 + run_time % 100 * 10) / 216e4
        as run_datetime
       ,CONVERT(DATETIME, RTRIM(run_date)) + (run_time * 9 + run_time % 10000 * 6 + run_time % 100 * 10) / 216e4 + (run_duration * 9 + run_duration % 10000 * 6 + run_duration % 100 * 10) / 216e4
        as run_ended
       ,h.instance_id
       ,h.job_id
       ,h.run_date
       ,h.run_time
   from msdb..sysjobhistory h INNER JOIN msdb..sysjobs j ON h.job_id = j.job_id
  WHERE h.step_id = 0
    and h.run_date >= @start_date
) x
;

SELECT
       t.job_name AS [Job/Step]
      ,' ' + CONVERT(varchar(19), t.run_datetime, 121) + ' to ' + CONVERT(varchar(19), t.run_ended, 121) AS Run_Msg
      ,0 as HdrDetail
      ,t.job_name
      ,t.run_datetime
      ,t.instance_id
      ,-1 as step_id
      ,jh.instance_id as actual_instance_id
  FROM #failhist t
         INNER JOIN msdb..sysjobhistory jh ON (    t.job_id = jh.job_id
                                               AND t.instance_id >= jh.instance_id
                                               AND (   t.run_date < jh.run_date
                                                    OR (    t.run_date = jh.run_date
                                                        AND t.run_time <= jh.run_time
                                                       )
                                                   )
                                              )
 WHERE jh.step_id = 0
UNION ALL
SELECT
       '        '
      +CASE
         WHEN jh.step_id = 0
           THEN 'Job Summary'
         WHEN jh.run_status BETWEEN 0 AND 1  -- summary
           THEN RIGHT(CAST(100 + jh.step_id as varchar),2) + ' - ' + jh.step_name
         ELSE   '  ' + RIGHT(CAST(100 + jh.step_id as varchar),2) + ' (add''l info)'
       END
      ,'        ' + CAST(jh.message as varchar(max))
      ,1
      ,t.job_name
      ,t.run_datetime
      ,t.instance_id
      ,jh.step_id
      ,jh.instance_id
  FROM #failhist t
         INNER JOIN msdb..sysjobhistory jh ON (    t.job_id = jh.job_id
                                               AND t.instance_id >= jh.instance_id
                                               AND (   t.run_date < jh.run_date
                                                    OR (    t.run_date = jh.run_date
                                                        AND t.run_time <= jh.run_time
                                                       )
                                                   )
                                              )
 ORDER BY run_datetime, instance_id, step_id
Run Code Online (Sandbox Code Playgroud)

如果您实际上有一个每秒运行一次以上的作业,则可能必须使用窗口函数,以确保您不会从具有相同 run_time 值的作业的早期运行中获取作业步骤。

警告:如果达到每个作业可以包含的行数限制sysjobhistory,您可能会得到奇怪/不完整的结果。另外,我偶尔会看到作业失败但没有生成作业结果(通常是暂时无法对运行作业的 Windows 用户进行身份验证)。