如何用Entity Framework获取SQL作业执行时间,能否获取指定作业最新执行时间?
当然可以通过Entity Framework检索SQL Server Agent作业的相关数据,包括指定作业的最近一次执行时间。SQL Server Agent的作业信息都存储在msdb数据库的系统表中,我们可以通过EF直接查询这些表,下面是两种常用的实现方式:
方法一:使用原生SQL查询(简单直接)
这种方式不需要额外配置实体映射,直接执行原生SQL语句来获取结果,适合只需要查询执行时间这类简单需求的场景。
首先,我们需要利用msdb.dbo.sysjobs(存储作业基本信息)和msdb.dbo.sysjobhistory(存储作业执行历史)这两个表,其中sysjobhistory的run_date(格式为YYYYMMDD整数)和run_time(格式为HHMMSS整数)字段需要转换为标准的DateTime格式。
示例代码(EF Core):
using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; // 假设你的DbContext实例为context var targetJobName = "你的目标作业名称"; var lastExecutionQuery = @" SELECT TOP 1 CONVERT(DATETIME, CONVERT(VARCHAR(8), run_date) + ' ' + STUFF(STUFF(RIGHT('000000' + CONVERT(VARCHAR(6), run_time), 6), 3, 0, ':'), 6, 0, ':')) AS LastExecutionTime FROM msdb.dbo.sysjobhistory jh JOIN msdb.dbo.sysjobs j ON jh.job_id = j.job_id WHERE j.name = @JobName AND jh.step_id = 0 -- step_id=0代表整个作业的执行记录(而非单个步骤) ORDER BY jh.run_date DESC, jh.run_time DESC"; var lastExecutionTime = await context.Database .SqlQuery<DateTime>(lastExecutionQuery, new SqlParameter("@JobName", targetJobName)) .FirstOrDefaultAsync(); if (lastExecutionTime != default) { Console.WriteLine($"作业最近执行时间:{lastExecutionTime}"); } else { Console.WriteLine("未找到该作业的执行记录"); }
代码说明:
step_id = 0:确保我们获取的是整个作业的执行汇总记录,而不是作业内单个步骤的执行记录。- 日期转换逻辑:把
run_date和run_time的整数格式拼接成标准的日期字符串,再转换为DateTime。
方法二:映射系统表到EF实体(适合复杂场景)
如果需要多次查询作业数据,或者需要获取作业的更多属性(比如作业描述、启用状态等),可以把msdb的系统表映射为EF实体,用LINQ进行查询。
1. 定义实体类
public class SysJob { public Guid job_id { get; set; } public string name { get; set; } public bool enabled { get; set; } // 可以根据需求添加其他字段,比如description、date_created等 } public class SysJobHistory { public Guid job_id { get; set; } public long instance_id { get; set; } // 复合主键的一部分 public int run_date { get; set; } public int run_time { get; set; } public int step_id { get; set; } // 可以添加其他字段,比如run_status(执行状态:0=失败,1=成功等) }
2. 在DbContext中配置表映射
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 映射sysjobs表到SysJob实体 modelBuilder.Entity<SysJob>() .ToTable("sysjobs", "dbo") .HasKey(j => j.job_id); // 映射sysjobhistory表到SysJobHistory实体(复合主键:job_id + instance_id) modelBuilder.Entity<SysJobHistory>() .ToTable("sysjobhistory", "dbo") .HasKey(jh => new { jh.job_id, jh.instance_id }); } // 在DbContext中添加DbSet public DbSet<SysJob> SysJobs { get; set; } public DbSet<SysJobHistory> SysJobHistories { get; set; }
3. 使用LINQ查询最近执行时间
var targetJobName = "你的目标作业名称"; var latestHistory = await (from job in context.SysJobs join history in context.SysJobHistories on job.job_id equals history.job_id where job.name == targetJobName && history.step_id == 0 orderby history.run_date descending, history.run_time descending select new { history.run_date, history.run_time }) .FirstOrDefaultAsync(); if (latestHistory != null) { // 转换整数格式的日期时间为DateTime string dateString = latestHistory.run_date.ToString("D8"); // 转为YYYYMMDD格式字符串 string timeString = latestHistory.run_time.ToString("D6") .Insert(4, ":") // 在第4位插入冒号,转为HHMM:SS .Insert(2, ":"); // 在第2位插入冒号,转为HH:MM:SS DateTime lastExecTime = DateTime.Parse($"{dateString} {timeString}"); Console.WriteLine($"作业最近执行时间:{lastExecTime}"); } else { Console.WriteLine("未找到该作业的执行记录"); }
重要注意事项
- 权限问题:执行查询的数据库用户需要拥有
msdb数据库的SELECT权限,你可以通过以下SQL语句授予权限:USE msdb; GRANT SELECT ON dbo.sysjobs TO [你的数据库用户名]; GRANT SELECT ON dbo.sysjobhistory TO [你的数据库用户名]; - 执行状态过滤:如果需要只查询成功的执行记录,可以在查询中添加
AND jh.run_status = 1(run_status=1代表执行成功)。
内容的提问来源于stack exchange,提问作者Adrian Borowiec
相关产品推荐
相关产品推荐

