I've seen also that sysjobactivity contains several lines per jobs.
For information i have implemented this using two stored procedures:
- the first one called "uspTECH_GET_JOB_RUNSTATE" to retrieve current status:
- the second one called "uspTECH_GET_JOB_FINALSTATUS" to retrieve the final status
(once the job has finished)
- an TSQL sql file named "exec_job_and_wait.sql" that:
execute sp_start_job
loop through uspTECH_GET_JOB_RUNSTATE while job is not finished
finally get the final status with "uspTECH_GET_JOB_FINALSTATUS"
This "exec_job_and_wait.sql" sql file is called for information by a batch dos called:
exec_job_and_wait.bat
I decided to compile those two stored proc. in master database.
For information, current user that invoke this batch dos must have the following rights on SQL Server database:
on master database:
Grant execute on both stored proc
GRANT EXECUTE ON [sys].[xp_sqlagent_enum_jobs]
on msdb database (to allow execution of jobs):
EXEC sp_droprolemember N'SQLAgentReaderRole', N'ToMe'
EXEC sp_droprolemember N'SQLAgentUserRole', N'ToMe'
EXEC sp_droprolemember N'SQLAgentOperatorRole', N'ToMe'
For information, i found those two stored proc. on internet, and i had a little bit adapt them:
Current status:
-- ======================================================================
-- Author:From Internet
-- Create date: 12/03/2009
-- Description:Allow to determin the status of a job (executing or not)
-- History:
-- . :
-- . :
--
-- Parameter:
-- Input:
-- . pIN_JobName : Job name to detect the status
-- . pIN_JobID : Job ID to detect the status
--
-- Output:
-- . pOUT_RunState : return the status of the job
--
-- ======================================================================
ALTER PROCEDURE [dbo].[uspTECH_GET_JOB_RUNSTATE]
@pIN_JobName sysname = NULL,
@pIN_JobID UNIQUEIDENTIFIER = NULL,
@pOUT_RunState int OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--------------------------------------------------------------------------------
-- DECLARE
--------------------------------------------------------------------------------
DECLARE @vErrCode INT
DECLARE @VErr_Stored_Proc nvarchar(126)
DECLARE @vErrline int
DECLARE @vErrMessage nvarchar(4000)
declare @vRc int
declare @vBeginTranCount int
DECLARE @is_sysadmin INT
DECLARE @vCpt INT
DECLARE @job_owner sysname
DECLARE @wrkJobID UNIQUEIDENTIFIER
DECLARE @wrkJobName sysname
--------------------------------------------------------------------------------
-- INITIALIZING
--------------------------------------------------------------------------------
-- variable locale commencant par v
SET @vErrCode = 0
set @vRc=0
SET @vErrCode = 0
set @vErrMessage ='';
set @vErrline=0;
set @VErr_Stored_Proc='' ;
set @vBeginTranCount= @@TRANCOUNT
--------------------------------------------------------------------------------
-- PROCESSING
--------------------------------------------------------------------------------
BEGIN TRY
-- SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0)
SET @is_sysadmin = 1
SELECT @job_owner = SUSER_SNAME()
IF (@pIN_JobName = N'')
SELECT @pIN_JobName = NULL
-- jobname or job id must be set
IF @pIN_JobName IS NULL
IF @pIN_JobID IS NULL
RETURN (-1) -- Need a job parameter
-- retrieve jobname from jobid
IF @pIN_JobName IS NULL
BEGIN
SELECT
@pIN_JobName = [name]
FROM
msdb.dbo.sysjobs_view
WHERE
job_id = @pIN_JobID
END
ELSE
BEGIN
-- retrieve jobid from jobname
IF @pIN_JobID IS NULL
SELECT
@pIN_JobID = job_ID
FROM
msdb.dbo.sysjobs_view
WHERE
[name] = @pIN_JobName
ELSE
BEGIN
SELECT
@wrkJobID = job_id,
@wrkJobName = [name]
FROM
msdb.dbo.sysjobs_view
WHERE
job_id = @pIN_JobID
IF IsNull(@wrkJobName,'') @pIN_JobName
RETURN (-2) -- @pIN_JobID does not match @pIN_JobName
END
END
IF @pIN_JobID IS NULL OR @pIN_JobName IS NULL
RETURN (-3) -- No job by that name/id on file
CREATE TABLE #xp_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 NULL,
running int NOT NULL, -- Boolean 1 Yes 0 No
current_step int NOT NULL,
current_retry_attempt int NOT NULL,
job_state int NOT NULL
)
INSERT INTO #xp_results
EXECUTE master.dbo.xp_sqlagent_enum_jobs @is_sysadmin, @job_owner, @pIN_JobID
IF @@ERROR 0
BEGIN
DROP TABLE #xp_results
RETURN (-4)
END
SELECT @pOUT_RunState = running
FROM #xp_results
WHERE
job_id = @pIN_JobID
set @vCpt = coalesce(@@rowcount,0)
IF @vCpt @vBeginTranCount ROLLBACK TRANSACTION
RAISERROR (@vErrMessage,16,1)
END CATCH
RETURN 0
END
Then Final status:
==============
-- ======================================================================
-- Author:From Internet
-- Create date: 23/03/2009
-- Description:Allow to determin the final status of a job once terminated
-- History:
-- . :
-- . :
--
-- Parameter:
-- Input:
-- . pIN_JobName : Job name to detect the status
-- . pIN_JobID : Job ID to detect the status
--
-- Output:
-- . pOUT_FinalStatus : return the status once finished
-- . pOUT_FinalStatus : description associe du status
--
-- ======================================================================
ALTER PROCEDURE [dbo].[uspTECH_GET_JOB_FINALSTATUS]
@pIN_JobName sysname = NULL,
@pIN_JobID UNIQUEIDENTIFIER = NULL,
@pOUT_FinalStatus int OUTPUT,
@pOUT_FinalStatusDesc VARCHAR(1024) OUTPUT,
@pIN_bDescriptionComplete int = 0
AS
BEGIN
DECLARE @is_sysadmin INT
DECLARE @job_owner sysname
DECLARE @wrkJobID UNIQUEIDENTIFIER
DECLARE @wrkJobName sysname
SET NOCOUNT ON
SELECT @is_sysadmin = ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0)
SELECT @job_owner = SUSER_SNAME()
IF (@pIN_JobName = N'')
SELECT @pIN_JobName = NULL
-- jobname or job id must be set
IF @pIN_JobName IS NULL
IF @pIN_JobID IS NULL
RETURN (-1) -- Need a job parameter
-- retrieve jobname from jobid
IF @pIN_JobName IS NULL
BEGIN
SELECT
@pIN_JobName = [name]
FROM
msdb.dbo.sysjobs_view
WHERE
job_id = @pIN_JobID
END
ELSE
BEGIN
-- retrieve jobid from jobname
IF @pIN_JobID IS NULL
SELECT
@pIN_JobID = job_ID
FROM
msdb.dbo.sysjobs_view
WHERE
[name] = @pIN_JobName
ELSE
BEGIN
SELECT
@wrkJobID = job_id,
@wrkJobName = [name]
FROM
msdb.dbo.sysjobs_view
WHERE
job_id = @pIN_JobID
IF IsNull(@wrkJobName,'') @pIN_JobName
RETURN (-2) -- @pIN_JobID does not match @pIN_JobName
END
END
IF @pIN_JobID IS NULL OR @pIN_JobName IS NULL
RETURN (-3) -- No job by that name/id on file
-- retourne le resultat du job
SELECT
@pOUT_FinalStatus = sjh.run_status,
@pOUT_FinalStatusDesc =
case @pIN_bDescriptionComplete
-- description succinte
when 0 then
case sjh.run_status
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 '
else 'Unknown '
end
when 1 then
CAST(SUBSTRING(sjh.message,1,1024) AS VARCHAR(1024))
end
FROM
msdb.dbo.sysjobhistory sjh
INNER JOIN
msdb.dbo.sysjobs_view sj
on sjh.job_id = sj.job_id
where sj.name = @pIN_JobName
and exists (
SELECT max_step
FROM (
SELECT max(sjhii.step_id) as max_step
FROM msdb.dbo.sysjobhistory sjhii
INNER JOIN msdb.dbo.sysjobs_view sjii
on sjhii.job_id = sjii.job_id
and sjii.name = @pIN_JobName
where (cast ( sjhii.run_date AS VARCHAR(10)) + dbo.FN_PAD_ZERO (sjhii.run_time, 6)) in
(
SELECT max(dt) from (
SELECT sjhi.job_id, sjhi.step_id, cast ( sjhi.run_date AS VARCHAR(10)) + dbo.FN_PAD_ZERO (sjhi.run_time, 6) dt
FROM msdb.dbo.sysjobhistory sjhi
INNER JOIN msdb.dbo.sysjobs_view sji
on sjhi.job_id = sji.job_id
and sji.name = @pIN_JobName
) I2
)
and cast ( sjhii.run_date AS VARCHAR(10)) + dbo.FN_PAD_ZERO (sjhii.run_time, 6) =
cast ( sjh.run_date AS VARCHAR(10)) + dbo.FN_PAD_ZERO (sjh.run_time, 6)
) I
where sjh.step_id = I.max_step
);
SET @pOUT_FinalStatus = COALESCE(@pOUT_FinalStatus,-1);
RETURN 0
END
---------------------------------------------------------------
An finally a TSQL code that i execute from a batch dos, that
- start the job
- execute the first SP until it is finished
- get the final status using the second stored proc.
TSQL stored in exec_job_and_wait.sql:
====================================
-- execute le job passe en parametre
DECLARE @ReturnCode INT
DECLARE @ReturnStatus INT
DECLARE @ReturnStatusDesc VARCHAR(50)
DECLARE @Job sysname
DECLARE @JobState int
DECLARE @Msg NVARCHAR(250)
SET @Job = N'$(V_JOBNAME)'
IF COALESCE(N'$(V_STEP)',N'') = N''
EXEC @ReturnCode = msdb.dbo.sp_start_job @job_name=@Job
ELSE
EXEC @ReturnCode = msdb.dbo.sp_start_job @job_name=@Job, @step_name=N'$(V_STEP)'
IF @ReturnCode 0
BEGIN
SET @Msg = 'Error execution du job "' + COALESCE(CAST( @Job AS VARCHAR(100) ),'') + '"'
RAISERROR(@Msg,16,1)
END
ELSE
BEGIN
print Convert(VARCHAR(30), GetDate(),121) + ': Lancement job("' + @Job + '")'
WaitFor Delay '00:00:02' -- Allow 2 seconds for job to start
SET @JobState = 1
WHILE @JobState 0 AND @ReturnCode = 0
BEGIN
print Convert(VARCHAR(30), GetDate(),121) + ': Attente Fin job("' + @Job + '")'
WaitFor Delay '00:00:05' -- Check every 05 seconds
Execute @ReturnCode = uspTECH_GET_JOB_RUNSTATE @pIN_JobName = @Job, @pOUT_RunState = @JobState OUTPUT
END
IF @ReturnCode 0
BEGIN
SET @Msg = 'Error attente du job "' + COALESCE(CAST( @Job AS VARCHAR(100) ),'') + '"'
RAISERROR(@Msg,16,1)
END
ELSE
BEGIN
Execute @ReturnCode = uspTECH_GET_JOB_FINALSTATUS @pIN_JobName = @Job,
@pOUT_FinalStatus = @ReturnStatus OUTPUT,
@pOUT_FinalStatusDesc = @ReturnStatusDesc OUTPUT
IF @ReturnCode 0
BEGIN
SET @Msg = 'Error recuperation final status du job "' + COALESCE(CAST( @Job AS VARCHAR(100) ),'') + '"'
RAISERROR(@Msg,16,1)
END
ELSE
print Convert(VARCHAR(30), GetDate(),121) + ': Status Final job("' + @Job + '")="' + + COALESCE(@ReturnStatusDesc,'') + '"'
-- 1 = Success
IF @ReturnStatus 1
BEGIN
SET @Msg = 'Error lors du job "' + COALESCE(CAST( @Job AS VARCHAR(100) ),'') + '", status final = "' + COALESCE(@ReturnStatusDesc,'') + '"'
RAISERROR(@Msg,16,1)
END
END
END
GO
==========================================
batch dos for information:
-------------------------
@echo off
set SQLCMDSERVER=%~1%
set SQLCMDDBNAME=master
set VAR_JOBNAME=%~2%
set VAR_STEP=%~3%
call :mydate
echo.
echo execution du job "%VAR_JOBNAME%" a l'etape "%VAR_STEP%"
sqlcmd -b -i exec_job_and_wait.sql -v V_JOBNAME="%VAR_JOBNAME%" -v V_STEP="%VAR_STEP%" -o "log\exec_job_and_wait_%VAR_JOBNAME%_%mydate%.log"
IF NOT %ERRORLEVEL% == 0 GOTO FIN_ERREUR_CD
:FIN
echo.
ECHO C est la fin correcte du lancement du job "%VAR_JOBNAME%" sur le serveur "%SQLCMDSERVER%" a partir de l'etape "%VAR_STEP%"
goto FINALL
:FIN_ERREUR_CD
echo.
ECHO !!! Erreur lors du lancement du job "%VAR_JOBNAME%" sur le serveur "%SQLCMDSERVER%" a partir de l'etape "%VAR_STEP%"
goto FINALL
:mydate
for /f "tokens=1-3 delims=/" %%a in ('date /T') do set jour=%%a&set mois=%%b&set anneedeb=%%c
for /f "tokens=1-3 delims=:" %%a in ('echo %time%') do set heures=%%a&set minutes=%%b&set secondesdeb=%%c
SET annee=%anneedeb: =%
SET secondes=%secondesdeb: =%
SET mydate=%username%_%annee%%mois%%jour%%heures%%minutes%%secondes%
:FINALL