我有一个安排工作的存储过程。这项工作需要很长时间才能完成(大约 30 到 40 分钟)。我需要了解这个 Job 的状态。以下详细信息将对我有所帮助
1) 如何查看已安排在未来某个时间但尚未开始的所有作业的列表
2) 如何查看正在运行的作业列表以及它们运行的时间跨度
3) 如何查看作业是否已成功完成或因任何错误而在其间停止。
我有一个安排工作的存储过程。这项工作需要很长时间才能完成(大约 30 到 40 分钟)。我需要了解这个 Job 的状态。以下详细信息将对我有所帮助
1) 如何查看已安排在未来某个时间但尚未开始的所有作业的列表
2) 如何查看正在运行的作业列表以及它们运行的时间跨度
3) 如何查看作业是否已成功完成或因任何错误而在其间停止。
我想指出,此页面上的任何 T-SQL 都不会准确工作,因为它们都不会加入syssessions表以仅获取当前会话,因此可能包含误报。
请参阅此内容以供参考:拥有空停止日期的工作意味着什么?
您还可以通过分析msdb中的sp_help_jobactivity过程来验证这一点。
我意识到这是关于 SO 的旧消息,但由于问题,我发现此消息仅部分有用。
SELECT
job.name,
job.job_id,
job.originating_server,
activity.run_requested_date,
DATEDIFF( SECOND, activity.run_requested_date, GETDATE() ) as Elapsed
FROM
msdb.dbo.sysjobs_view job
JOIN
msdb.dbo.sysjobactivity activity
ON
job.job_id = activity.job_id
JOIN
msdb.dbo.syssessions sess
ON
sess.session_id = activity.session_id
JOIN
(
SELECT
MAX( agent_start_date ) AS max_agent_start_date
FROM
msdb.dbo.syssessions
) sess_max
ON
sess.agent_start_date = sess_max.max_agent_start_date
WHERE
run_requested_date IS NOT NULL AND stop_execution_date IS NULL
您可以尝试使用系统存储过程 sp_help_job。这将返回有关作业、其步骤、时间表和服务器的信息。例如
EXEC msdb.dbo.sp_help_job @Job_name = 'Your Job Name'
SQL 联机丛书应包含有关它返回的记录的大量信息。
要返回有关多个作业的信息,您可以尝试查询以下系统表,其中包含有关作业的各种信息
它们的名称是不言自明的(除了 SysJobServers,它保存有关作业上次运行时间和结果的信息)。
同样,可以在 MSDN 上找到有关字段的信息。例如,查看SysJobs页面
这就是我用来获取正在运行的作业的内容(主要是为了让我可以杀死那些可能已经挂起的作业):
SELECT
job.Name, job.job_ID
,job.Originating_Server
,activity.run_requested_Date
,datediff(minute, activity.run_requested_Date, getdate()) AS Elapsed
FROM
msdb.dbo.sysjobs_view job
INNER JOIN msdb.dbo.sysjobactivity activity
ON (job.job_id = activity.job_id)
WHERE
run_Requested_date is not null
AND stop_execution_date is null
AND job.name like 'Your Job Prefix%'
正如 Tim 所说,MSDN / BOL 文档在 sysjobsX 表的内容上相当不错。请记住它们是 MSDB 中的表。
-- Microsoft SQL Server 2008 Standard Edition:
IF EXISTS(SELECT 1
FROM msdb.dbo.sysjobs J
JOIN msdb.dbo.sysjobactivity A
ON A.job_id=J.job_id
WHERE J.name=N'Your Job Name'
AND A.run_requested_date IS NOT NULL
AND A.stop_execution_date IS NULL
)
PRINT 'The job is running!'
ELSE
PRINT 'The job is not running.'
我们可以通过多种方式查询 msdb 以获取详细信息。
少数是
select job.Name, job.job_ID, job.Originating_Server,activity.run_requested_Date,
datediff(minute, activity.run_requested_Date, getdate()) as Elapsed
from msdb.dbo.sysjobs_view job
inner join msdb.dbo.sysjobactivity activity on (job.job_id = activity.job_id)
where run_Requested_date is not null
and stop_execution_date is null
and job.name like 'Your Job Prefix%'
这将显示上次运行状态/时间,或者如果正在运行,它会显示当前运行时间、步骤编号/信息和 SPID(如果它具有关联的 SPID)。它还显示启用/禁用和作业用户,其中它为未解析的用户帐户转换为 NT SID 格式。
CREATE TABLE #list_running_SQL_jobs
(
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 NULL
, running INT NOT NULL
, current_step INT NOT NULL
, current_retry_attempt INT NOT NULL
, job_state INT NOT NULL
);
DECLARE @sqluser NVARCHAR(128)
, @is_sysadmin INT;
SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0);
DECLARE read_sysjobs_for_running CURSOR FOR
SELECT DISTINCT SUSER_SNAME(owner_sid)FROM msdb.dbo.sysjobs;
OPEN read_sysjobs_for_running;
FETCH NEXT FROM read_sysjobs_for_running
INTO @sqluser;
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO #list_running_SQL_jobs
EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, @sqluser;
FETCH NEXT FROM read_sysjobs_for_running
INTO @sqluser;
END;
CLOSE read_sysjobs_for_running;
DEALLOCATE read_sysjobs_for_running;
SELECT j.name
, 'Enbld' = CASE j.enabled
WHEN 0
THEN 'no'
ELSE 'YES'
END
, '#Min' = DATEDIFF(MINUTE, a.start_execution_date, ISNULL(a.stop_execution_date, GETDATE()))
, 'Status' = CASE
WHEN a.start_execution_date IS NOT NULL
AND a.stop_execution_date IS NULL
THEN 'Executing'
WHEN h.run_status = 0
THEN 'FAILED'
WHEN h.run_status = 2
THEN 'Retry'
WHEN h.run_status = 3
THEN 'Canceled'
WHEN h.run_status = 4
THEN 'InProg'
WHEN h.run_status = 1
THEN 'Success'
ELSE 'Idle'
END
, r.current_step
, spid = p.session_id
, owner = ISNULL(SUSER_SNAME(j.owner_sid), 'S-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 1))) - CONVERT(BIGINT, 256) * CONVERT(BIGINT, UNICODE(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 1)) / 256)) + '-' + CONVERT(NVARCHAR(12), UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 4), 1)) / 256 + CONVERT(BIGINT, NULLIF(UNICODE(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 1)) / 256, 0)) - CONVERT(BIGINT, UNICODE(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 1)) / 256)) + ISNULL('-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 5), 1))) + CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 6), 1))) * CONVERT(BIGINT, 65536) + CONVERT(BIGINT, NULLIF(SIGN(LEN(CONVERT(NVARCHAR(256), j.owner_sid)) - 6), -1)) * 0), '') + ISNULL('-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 7), 1))) + CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 8), 1))) * CONVERT(BIGINT, 65536) + CONVERT(BIGINT, NULLIF(SIGN(LEN(CONVERT(NVARCHAR(256), j.owner_sid)) - 8), -1)) * 0), '') + ISNULL('-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 9), 1))) + CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 10), 1))) * CONVERT(BIGINT, 65536) + CONVERT(BIGINT, NULLIF(SIGN(LEN(CONVERT(NVARCHAR(256), j.owner_sid)) - 10), -1)) * 0), '') + ISNULL('-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 11), 1))) + CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 12), 1))) * CONVERT(BIGINT, 65536) + CONVERT(BIGINT, NULLIF(SIGN(LEN(CONVERT(NVARCHAR(256), j.owner_sid)) - 12), -1)) * 0), '') + ISNULL('-' + CONVERT(NVARCHAR(12), CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 13), 1))) + CONVERT(BIGINT, UNICODE(RIGHT(LEFT(CONVERT(NVARCHAR(256), j.owner_sid), 14), 1))) * CONVERT(BIGINT, 65536) + CONVERT(BIGINT, NULLIF(SIGN(LEN(CONVERT(NVARCHAR(256), j.owner_sid)) - 14), -1)) * 0), '')) --SHOW as NT SID when unresolved
, a.start_execution_date
, a.stop_execution_date
, t.subsystem
, t.step_name
FROM msdb.dbo.sysjobs j
LEFT OUTER JOIN (SELECT DISTINCT * FROM #list_running_SQL_jobs) r
ON j.job_id = r.job_id
LEFT OUTER JOIN msdb.dbo.sysjobactivity a
ON j.job_id = a.job_id
AND a.start_execution_date IS NOT NULL
--AND a.stop_execution_date IS NULL
AND NOT EXISTS
(
SELECT *
FROM msdb.dbo.sysjobactivity at
WHERE at.job_id = a.job_id
AND at.start_execution_date > a.start_execution_date
)
LEFT OUTER JOIN sys.dm_exec_sessions p
ON p.program_name LIKE 'SQLAgent%0x%'
AND j.job_id = SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 7, 2) + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 5, 2) + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 3, 2) + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 1, 2) + '-' + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 11, 2) + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 9, 2) + '-' + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 15, 2) + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 13, 2) + '-' + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 17, 4) + '-' + SUBSTRING(SUBSTRING(p.program_name, CHARINDEX('0x', p.program_name) + 2, 32), 21, 12)
LEFT OUTER JOIN msdb.dbo.sysjobhistory h
ON j.job_id = h.job_id
AND h.instance_id = a.job_history_id
LEFT OUTER JOIN msdb.dbo.sysjobsteps t
ON t.job_id = j.job_id
AND t.step_id = r.current_step
ORDER BY 1;
DROP TABLE #list_running_SQL_jobs;
您尚未指定希望如何查看这些详细信息。
对于第一眼,我建议检查Server Management Studio。
您可以在“作业”下的 SQL Server 代理部分中查看作业和当前状态。如果您选择一个工作,“属性”页面会显示一个指向“工作历史”的链接,您可以在其中查看开始和结束时间、是否有任何错误、哪个步骤导致了错误等等。
您可以指定警报和通知,以便在作业成功完成或失败时向您发送电子邮件或寻呼您。
有一个作业活动监视器,但实际上我从未使用过它。你可以试一试。
如果你想通过 T-SQL 检查它,那么我不知道你该怎么做。
SELECT sj.name
FROM msdb..sysjobactivity aj
JOIN msdb..sysjobs sj
on sj.job_id = aj.job_id
WHERE aj.stop_execution_date IS NULL -- job hasn't stopped running
AND aj.start_execution_date IS NOT NULL -- job is currently running
AND sj.name = '<your Job Name>'
AND NOT EXISTS( -- make sure this is the most recent run
select 1
from msdb..sysjobactivity new
where new.job_id = aj.job_id
and new.start_execution_date > aj.start_execution_date ) )
print 'running'
这是一个老问题,但我刚刚遇到了类似的情况,我需要检查 SQL Server 上的作业状态。很多人提到 sysjobactivity 表并指出 MSDN 文档很棒。但是,我还想强调一下作业活动监视器,它提供了在您的服务器上定义的所有作业的状态。
我使用评分最高的答案创建了一个简单的 SQL 函数来检查 SQL 代理作业是否已经在运行:
-- ===================================================================================
-- Function: "IsJobAlreadyRunning" | Author: Geoff Griswald | Created: 2021-05-06
-- Description: Check if a SQL Agent Job is already Running - Return 1 if Yes, 0 if No
-- ===================================================================================
CREATE FUNCTION dbo.IsJobAlreadyRunning (@AgentJobName varchar(140))
RETURNS bit
AS
BEGIN
DECLARE @Result bit = 0
IF EXISTS (SELECT job.name
FROM msdb.dbo.sysjobs_view job
INNER JOIN msdb.dbo.sysjobactivity activity ON job.job_id = activity.job_id
INNER JOIN msdb.dbo.syssessions sess ON sess.session_id = activity.session_id
INNER JOIN (SELECT MAX(agent_start_date) AS max_agent_start_date
FROM msdb.dbo.syssessions) sess_max ON sess.agent_start_date = sess_max.max_agent_start_date
WHERE run_requested_date IS NOT NULL
AND stop_execution_date IS NULL
AND job.name = @AgentJobName)
SET @Result = 1
RETURN @Result
END;
这很有用,因为我可以调用此函数并在我尝试启动它之前检查我想要启动的作业是否已经在运行,而不会给我的代码添加大量臃肿。例如:
DECLARE @JobName varchar(140) = 'MyAgentJobName'
IF (SELECT dbo.IsJobAlreadyRunning(@JobName)) = 0
EXEC msdb.dbo.sp_start_job @JobName
上面的任务有效,但我在 msdb.dbo.sysjobactivity 中看到了许多记录, 其中 run_Requested_date 不为空,stop_execution_date 为空 ----并且作业当前未运行。
我建议运行以下脚本来清除所有虚假条目(确保当时没有作业正在运行)。
SQL2008:
delete activity
from msdb.dbo.sysjobs_view job
inner join msdb.dbo.sysjobactivity activity on job.job_id = activity.job_id
where
activity.run_Requested_date is not null
and activity.stop_execution_date is null
我在我的一台服务器上查询 MSDB 表(也就是上面列出的代码)时遇到了问题,因为我的一项工作会开始运行,但事实并非如此。有一个系统存储过程可以返回执行状态,但是不能执行插入 exec 语句而不会出错。里面是另一个可以与 insert exec 语句一起使用的系统存储过程。
INSERT INTO #Job
EXEC master.dbo.xp_sqlagent_enum_jobs 1,dbo
以及将其加载到的表:
CREATE TABLE #Job
(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)
下面的脚本获取服务器上每个作业的作业状态。它还告诉那里有多少步,当前正在运行的步和经过的时间是什么。
SELECT sj.Name,
CASE
WHEN sja.start_execution_date IS NULL THEN 'Never ran'
WHEN sja.start_execution_date IS NOT NULL AND sja.stop_execution_date IS NULL THEN 'Running'
WHEN sja.start_execution_date IS NOT NULL AND sja.stop_execution_date IS NOT NULL THEN 'Not running'
END AS 'RunStatus',
CASE WHEN sja.start_execution_date IS NOT NULL AND sja.stop_execution_date IS NULL then js.StepCount else null end As TotalNumberOfSteps,
CASE WHEN sja.start_execution_date IS NOT NULL AND sja.stop_execution_date IS NULL then ISNULL(sja.last_executed_step_id+1,js.StepCount) else null end as currentlyExecutingStep,
CASE WHEN sja.start_execution_date IS NOT NULL AND sja.stop_execution_date IS NULL then datediff(minute, sja.run_requested_date, getdate()) ELSE NULL end as ElapsedTime
FROM msdb.dbo.sysjobs sj
JOIN msdb.dbo.sysjobactivity sja
ON sj.job_id = sja.job_id
CROSS APPLY (SELECT COUNT(*) FROM msdb.dbo.sysjobsteps as js WHERE js.job_id = sj.job_id) as js(StepCount)
WHERE session_id = (
SELECT MAX(session_id) FROM msdb.dbo.sysjobactivity)
ORDER BY RunStatus desc
我发现最简单的方法是创建一个存储过程。输入“工作名称”并点击开始。
/*-----------------------------------------------------------------------------------------------------------
Document Title: usp_getJobStatus
Purpose: Finds a Current Jobs Run Status
Input Example: EXECUTE usp_getJobStatus 'MyJobName'
-------------------------------------------------------------------------------------------------------------*/
IF OBJECT_ID ( 'usp_getJobStatus','P' ) IS NOT NULL
DROP PROCEDURE usp_getJobStatus;
GO
CREATE PROCEDURE usp_getJobStatus
@JobName NVARCHAR (1000)
AS
IF OBJECT_ID('TempDB..#JobResults','U') IS NOT NULL DROP TABLE #JobResults
CREATE TABLE #JobResults ( 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 )
INSERT #JobResults
EXECUTE master.dbo.xp_sqlagent_enum_jobs 1, '';
SELECT job.name AS [Job_Name],
( SELECT MAX(CAST( STUFF(STUFF(CAST(jh.run_date AS VARCHAR),7,0,'-'),5,0,'-') + ' ' +
STUFF(STUFF(REPLACE(STR(jh.run_time,6,0),' ','0'),5,0,':'),3,0,':') AS DATETIME))
FROM msdb.dbo.sysjobs AS j
INNER JOIN msdb.dbo.sysjobhistory AS jh
ON jh.job_id = j.job_id AND jh.step_id = 0
WHERE j.[name] LIKE '%' + @JobName + '%'
GROUP BY j.[name] ) AS [Last_Completed_DateTime],
( SELECT TOP 1 start_execution_date
FROM msdb.dbo.sysjobactivity
WHERE job_id = r.job_id
ORDER BY start_execution_date DESC ) AS [Job_Start_DateTime],
CASE
WHEN r.running = 0 THEN
CASE
WHEN jobInfo.lASt_run_outcome = 0 THEN 'Failed'
WHEN jobInfo.lASt_run_outcome = 1 THEN 'Success'
WHEN jobInfo.lASt_run_outcome = 3 THEN 'Canceled'
ELSE 'Unknown'
END
WHEN r.job_state = 0 THEN 'Success'
WHEN r.job_state = 4 THEN 'Success'
WHEN r.job_state = 5 THEN 'Success'
WHEN r.job_state = 1 THEN 'In Progress'
WHEN r.job_state = 2 THEN 'In Progress'
WHEN r.job_state = 3 THEN 'In Progress'
WHEN r.job_state = 7 THEN 'In Progress'
ELSE 'Unknown' END AS [Run_Status_Description]
FROM #JobResults AS r
LEFT OUTER JOIN msdb.dbo.sysjobservers AS jobInfo
ON r.job_id = jobInfo.job_id
INNER JOIN msdb.dbo.sysjobs AS job
ON r.job_id = job.job_id
WHERE job.[enabled] = 1
AND job.name LIKE '%' + @JobName + '%'
;WITH CTE_JobStatus
AS (
SELECT DISTINCT NAME AS [JobName]
,s.step_id
,s.step_name
,CASE
WHEN [Enabled] = 1
THEN 'Enabled'
ELSE 'Disabled'
END [JobStatus]
,CASE
WHEN SJH.run_status = 0
THEN 'Failed'
WHEN SJH.run_status = 1
THEN 'Succeeded'
WHEN SJH.run_status = 2
THEN 'Retry'
WHEN SJH.run_status = 3
THEN 'Cancelled'
WHEN SJH.run_status = 4
THEN 'In Progress'
ELSE 'Unknown'
END [JobOutcome]
,CONVERT(VARCHAR(8), sjh.run_date) [RunDate]
,CONVERT(VARCHAR(8), STUFF(STUFF(CONVERT(TIMESTAMP, RIGHT('000000' + CONVERT(VARCHAR(6), sjh.run_time), 6)), 3, 0, ':'), 6, 0, ':')) RunTime
,RANK() OVER (
PARTITION BY s.step_name ORDER BY sjh.run_date DESC
,sjh.run_time DESC
) AS rn
,SJH.run_status
FROM msdb..SYSJobs sj
INNER JOIN msdb..SYSJobHistory sjh ON sj.job_id = sjh.job_id
INNER JOIN msdb.dbo.sysjobsteps s ON sjh.job_id = s.job_id
AND sjh.step_id = s.step_id
WHERE (sj.NAME LIKE 'JOB NAME')
AND sjh.run_date = CONVERT(CHAR, getdate(), 112)
)
SELECT *
FROM CTE_JobStatus
WHERE rn = 1
AND run_status NOT IN (1,4)