初识SQL Server代理&作业

Posted MelonSuika

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初识SQL Server代理&作业相关的知识,希望对你有一定的参考价值。

文章目录

一、前言

别人写的程序在数据库中生成了一些表,并且会不停更新表中数据。

现在有个需求,定期根据表中的数据做一些运算,然后重新生成一张表。我将新表的表名、字段给第三方使用。

关键是,我无法修改别人的代码,好在我可以登录该系统所运行电脑的数据库。
经过一番搜索后,我发现了触发器和SQL Server代理这两种工具。进一步分析后,觉得SQL Server代理可能更符合我的需求。

❗注意
本文不讨论多SQL Server实例的情况。
由于使用场景较简单,本文只简述SQL Server代理及其组件,有关SQL Server代理安全性等拓展内容不做介绍。


二、SQL Server代理

SQL Server代理是一种微软Windows服务,用于执行一些预先安排好的任务,这些任务在SQL Server中称为jobs(作业)。

1. SQL Server代理的好处

SQL Server代理使用了SQL Server来存储作业信息。作业包含一个或多个job steps(作业步骤)。每个步骤都包含了自己的任务,例如,备份数据库。

SQL Server代理可以执行计划(就是指定执行时间点)上或用于响应特定事件的作业。例如,若你想在每个工作日的下班后备份公司服务器,你就可以使该任务自动化执行。比如,将该备份任务安排在周一到周五的22:00之后运行。若备份过程遇到问题,SQL Server代理还能记录事件并通知你。

至此,SQL Server Agent(代理)的功能很清晰了。

它可以替数据库操作员来处一些和SQL Server相关的事情。

❗注意
默认情况下,安装SQL Server时,SQL Server代理服务是禁用的,除非用户选择自动启动该服务。所以你要手动启动它。

2. SQL Server代理组件

SQL Server代理使用以下组件来定义要执行的任务、何时执行任务以及如何报告任务是否成功执行。

⭐2.1. 作业 Jobs & 作业步骤 Job Steps

作业是SQL Server代理执行的一系列指定操作(这个一系列很重要,作业有点类似操作的容器,里面放了许多操作,所以是管理单元,而不是真正的执行单元。而这一个个操作,就是下文的作业步骤)。使用作业可以定义各种任务,并配置它们的执行次数以及监视它们的执行状态与执行结果。作业可以在本地服务器上运行,也可以在远程服务器上运行。

可以用以下几种方式来执行作业:

  • 按照计划表配置执行。
  • 响应警报来执行。
  • 通过执行sp_start_job存储过程。

作业中的每个操作都称为 job step(作业步骤)
作业步骤是作业在数据库或服务器上执行的操作。每个作业必须至少有一个作业步骤(不然,空有一个容器,啥也做不了)。

作业步骤可以是:

  • 可执行程序和操作系统命令。
  • Transact-SQL(T-SQL)语句,包括存储过程和扩展存储过程。
  • PowerShell脚本。
  • Mircosoft ActiveX脚本。
  • 复制任务。
  • 分析服务任务。
  • 集成服务包。

每个作业步骤都运行在专门的安全上下文(环境)中。对于使用T-SQL的作业步骤,使用EXECUTE AS语句为作业步骤设置安全上下文。其他类型的作业步骤,使用代理账户为作业步骤设置安全上下文。

⭐2.2. 计划 Schedules

计划(表)(schedule,其实在计算机中,我更喜欢叫它调度)指定了作业何时运行。在一个计划上可以运行多个作业,一个作业也可以被多个计划应用。计划可以定义作业运行的时间条件,如:

  • SQL Server代理启动时运行。
  • 当计算机CPU空闲时运行(何为空闲由你定义)。
  • 在某个特定的日期和时间运行一次。
  • 在一个循环的计划表上(比如,每天早上8:00)。

2.3. 警报 Alerts

警报(alert) 是对特定事件的自动响应。事件可以是启动的作业或达到阈值的系统资源。你可以定义警报发生的条件。

警报可以响应以下条件:

  • SQL Server 事件。
  • SQL Server 性能条件。
  • 运行在SQL Server代理的计算机上的WMI事件。

警报可以执行以下操作:

  • 通知一个或多个操作员(下节中介绍)。
  • 执行一个作业。

2.4. 操作员 Operators

操作员(operator) 为负责维护SQL Server实例的个人定义了联系方式。在一些企业中,操作员职责被分配到了个人(责任到人)。尤其在有多个服务器的企业中,许多人可以分担操作员的责任。操作员不包含安全信息,也不定义安全主体。

SQL Server能通过以下方式通知操作员:

  • 电子邮件
  • 寻呼机(通过电子邮件)
  • 网络发送

❗注意
如要使用网络发送通知,必须在SQL Server代理所在的计算机上启动Windows Messenger服务。
如果发现寻呼机和网络发送的功能无法使用,请不要惊讶。因为微软早就计划从SQL Server代理中删除这两个功能了。

若要使用电子邮件或传呼机向操作员发送通知,必须将SQL Server代理配置为使用数据库邮件。可以将操作员定义成一组个人的别名。这样,该别名的所有成员就不会同时被验证。

⭐3. 相关工作

要使SQL Server管理更加自动化,通常要做以下工作:

  1. 确定哪些管理任务或服务器事件会定期发生,以及这些任务或事件是否可以通过编程方式管理。若任务涉及一个可预测的步骤序列,并且发生在特定的时间或响应特定事件,那么它是一个很好的自动化候选任务。
  2. 用SQL Server管理工具(SSMS)、Transact-SQL脚本或SQL Server管理对象(SMO)定义一系列作业、计划表、警报和操作员。
  3. 运行定义的SQL Server代理作业。

❗注意
对于SQL Server默认实例,SQL Server服务的名称为SQLSERVERAGENT。
对于命名了的实例,SQL Server代理服务名为SQLAgent$instancename。

在对代理有了一定了解后,再介绍一个例子,在代理中创建一个作业,并添加一些脚本代码。


三、创建并执行作业

本节讲解如何在SQL Server Management Studio(SSMS)中创建一个SQL Server代理作业,并在计划中运行它。(创建作业(包括其他组件,计划、警报等等)的方式有很多,可以用SSMS图形化操作,可以用Transact-SQL脚本操作,也可以用SQL Server Management Objects操作,这边介绍SSMS图形化操作,该方式比较通用直观)

1. 创建作业

在SSMS中创建一个作业,通常经过以下几步:

  1. 对象资源管理器中,单击加号展开要在其中创建SQL Server代理作业的服务器。

  2. 点击加号展开SQL Server代理

  3. 右键单击作业文件夹并选择新建作业

  4. 新建作业对话框中的常规页面,修改作业的常规属性。(一般改个名称即可)

  5. 步骤页面,组织作业步骤。
    这边着重介绍下T-SQL类型的作业步骤。创建T-SQL作业步骤时,你必须:

    • 指定运行该作业的数据库。
    • 键入执行的T-SQL语句。语句可以调用存储过程或扩展存储过程。

    你也可以选一个现有的T-SQL文件作为作业步骤的命令。
    T-SQL作业步骤不使用SQL Server代理作为代理人。取而代之,作业步骤作为作业步骤的所有者运行(?)。

  6. 计划页面,组织作业计划。
    你可以选取一个已有的计划或新建一个计划。当与计划绑定后,该作业就会按计划的时间执行。可见,作业和计划是独立的,作业定义了任务内容,计划定义了执行时间。

  7. 警报页面,组织作业警报。

  8. 通知页面,设置代理在作业完成时执行的操作。

  9. 目标页面,管理作业目标服务器。

  10. 配置完成, 点击确定。

2. 执行作业

在作业的常规页面中,有个启用选项:

勾选启用后,给作业绑定计划或警报,达到条件后(达到计划指定时间或触发警报),作业就会执行。


四、结语

SQL Server代理在实际开发中作用很大,许多操作都可以放到SQL Server代理中执行,很多时候能事半功倍。

本文对SQL Server代理的基本组件和创建作业绑定计划的简单使用场景进行介绍。实际使用中的重头戏应该是脚本的编写。

从存储过程执行 SQL Server 代理作业并返回作业结果

【中文标题】从存储过程执行 SQL Server 代理作业并返回作业结果【英文标题】:Executing SQL Server Agent Job from a stored procedure and returning job result 【发布时间】:2012-08-28 06:16:33 【问题描述】:

需要有一个存储过程调用 SQL Server 代理作业并返回作业是否成功运行。

目前为止

CREATE PROCEDURE MonthlyData
AS
EXEC msdb.dbo.sp_start_job N'MonthlyData'

WAITFOR DELAY '000:04:00'

EXEC msdb.dbo.sp_help_jobhistory @job_name = 'MonthlyData'
GO

从哪个开始作业,如果作业运行成功与否,最好的返回方式是什么?

Ok 进行了编辑并使用了 WAITFOR DELAY,因为该作业通常在 3-4 分钟之间运行,从不超过 4 分钟。该作业是否有效,但有更有效的方法吗?

【问题讨论】:

好问题。由于“等待代理工作完成”被定向到这个问题,但你的问题实际上并没有提到它,我正在为 SEO 添加这个评论:) 【参考方案1】:

您可以运行查询:

EXEC msdb.dbo.sp_help_jobhistory 
    @job_name = N'MonthlyData'

它将返回一个列 run_status。状态是:

 0 - Failed
 1 - Succeeded
 2 - Retry
 3 - Canceled         

更多信息MSDN

编辑:您可能想要轮询您的工作并确保它已被执行。您可以从 sp_help_job 过程中获取此信息。当此过程返回4 状态时,表示作业处于空闲状态。 然后就可以安全地检查它的运行状态了。

您可以使用以下代码进行投票:

DECLARE @job_status INT
SELECT @job_status = current_execution_status FROM OPENROWSET('SQLNCLI', 'Server=.;Trusted_Connection=yes;','exec msdb.dbo.sp_help_job @job_name = ''NightlyBackups''')

WHILE @job_status <> 4
BEGIN
    WAITFOR DELAY '00:00:03'
    SELECT @job_status = current_execution_status FROM OPENROWSET('SQLNCLI', 'Server=.;Trusted_Connection=yes;','exec msdb.dbo.sp_help_job @job_name = ''NightlyBackups''')
END

EXEC msdb.dbo.sp_help_jobhistory 
    @job_name = N'NightlyBackups' ;
GO

此代码将检查状态,等待 3 秒,然后重试。一旦我们获得 4 的状态,我们就知道工作已完成,并且可以安全地检查工作历史记录。

【讨论】:

您可能想表明您基本上需要轮询,直到相关工作达到“最终状态”。 所以一旦我开始工作,我就需要轮询直到工作完成,然后执行 jobhistory? @ChristianK 你能告诉我们更多关于 Job Agent 投票的信息吗? 这段代码有点问题。在工作实际开始并修改其@job_status 之前,sp_start_job 之后可能会有延迟。有时当我使用此方法时,循环会立即终止,因为 @job_status 与上次运行时相比仍为 4。一种解决方法是在检查初始作业状态之前放置WAITFOR DELAY '00:00:04',以使 SQL 代理有机会启动作业。不过感觉很hackish。还有其他想法吗? 这个人遇到了同样的问题,并得到了与我相同的(黑客)解决方案。 interworks.com/blogs/bbickell/2010/01/15/…【参考方案2】:

对于所有不允许使用 OPENROWSET 命令的人,这可能会有所帮助。我在这里找到了解决方案的起点:

http://social.msdn.microsoft.com/Forums/en-US/89659729-fea8-4df0-8057-79e0a437b658/dynamically-checking-job-status-with-tsql

这取决于 msdb.dbo.sysjobactivity 表的某些列在作业以一种或另一种方式完成后首先填充的事实。

-- Start job
DECLARE @job_name NVARCHAR(MAX) = 'JobName'
EXEC msdb.dbo.sp_start_job @job_name = @job_name


-- Wait for job to finish
DECLARE @job_history_id AS INT = NULL

WHILE @time_constraint = @ok
BEGIN
    SELECT TOP 1 @job_history_id = activity.job_history_id
    FROM msdb.dbo.sysjobs jobs
    INNER JOIN msdb.dbo.sysjobactivity activity ON activity.job_id = jobs.job_id
    WHERE jobs.name = @job_name
    ORDER BY activity.start_execution_date DESC

    IF @job_history_id IS NULL
    BEGIN
        WAITFOR DELAY '00:00:10'
        CONTINUE
    END
    ELSE
        BREAK
END


-- Check exit code
SELECT history.run_status
FROM msdb.dbo.sysjobhistory history
WHERE history.instance_id = @job_history_id

您可能需要检查允许 WHILE 循环运行多长时间。我选择将该部分排除在示例之外。

Microsoft 退出代码等指南: http://technet.microsoft.com/en-us/library/ms174997.aspx

【讨论】:

我喜欢你的代码,但我不喜欢超时。我已经发布了我自己的没有超时的解决方案。代理作业步骤可以配置超时,我认为这是我希望拥有它们的地方。【参考方案3】:

我可能有点晚了,但我发现以下查询对我有用。它将给出执行时间和执行结束时间。您也可以更改它以获取状态。

SELECT 
    job.name, 
    job.job_id, 
    job.originating_server, 
    activity.run_requested_date, 
    activity.stop_execution_date, 
    DATEDIFF( SECOND, activity.run_requested_date, activity.stop_execution_date ) 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 
AND job.name = @JobName

【讨论】:

【参考方案4】:

这是一个脚本,它将检查作业的状态,如果它尚未运行,则会运行它。

declare @xp_results table (
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)

DECLARE @job_id uniqueidentifier ;
select @job_id = job_id from msdb.dbo.sysjobs where name = 'Job1';
insert into @xp_results
EXEC master.dbo.xp_sqlagent_enum_jobs 1, sa, @job_id

select case when running = 1 then 'Currently Running' else '' end as running, 
case job_state 
    when 0 then 'Not Idle or Suspended'
    when 1 then 'Executing Job'
    when 2 then 'Waiting For Thread'
    when 3 then 'Between Retries'
    when 4 then 'Idle'
    when 5 then 'Suspended'
    when 6 then 'WaitingForStepToFinish'
    when 7 then 'PerformingCompletionActions'
end as job_state
from @xp_results

IF (select running from @xp_results) <> 1
    EXEC msdb.dbo.sp_start_job 'Job1'

【讨论】:

【参考方案5】:

@lapponiandevil 在这里有最好、最有用的解决方案,但是他们的代码比它需要的稍微复杂一些,并且实际上并不能按原样工作,因为它需要 @Time_constraint 和 @ok 变量,这些变量在显示代码。

这些是为了支持代理作业的超时,但这不是必需的。代理作业步骤可以配置有自己的超时值,如果超过它会正确出错,这与他们的代码不同。如果您使用该超时方法,您可能会发现自己正在寻找幻像错误,或者在您进入流程的下一步时不知道您正在等待的代理作业仍在运行。

我认为将代码减少到最低限度并删除超时功能是理想的。这是我根据他们的解决方案提出的:

-- Start Agent Job
DECLARE @JobName NVARCHAR(128) = 'My Agent Job Name'
   EXEC msdb.dbo.sp_start_job @JobName

-- Wait for Agent Job to finish
DECLARE @HistoryID AS INT = NULL
  WHILE @HistoryID IS NULL
  BEGIN
        --Read most recent Job History ID for the specified Agent Job name
            SELECT TOP 1 @HistoryID = b.job_history_id
              FROM msdb.dbo.sysjobs a
        INNER JOIN msdb.dbo.sysjobactivity b ON b.job_id = a.job_id
             WHERE a.name = @JobName
          ORDER BY b.Start_execution_date DESC
        --If Job is still running (Job History ID = NULL), wait 3 seconds
                IF @HistoryID IS NULL WAITFOR DELAY '00:00:03'
    END

-- Check Agent Job exit code to make sure it succeeded
     IF (SELECT run_status 
           FROM msdb.dbo.sysjobhistory 
          WHERE instance_id = @HistoryID) <> 1 
  THROW 69000, 'Child Agent Job failure', 1;  

【讨论】:

这对我有帮助 - 我只有一个注释:我必须将 IF... DELAY... 语句移动到块的顶部,就在“BEGIN ...”下方,以便它在选择之前等待第一次。否则,在第一次迭代时,系统信息尚未创建,因此它从之前的运行中获取 stop_execution_date,因此永远不会等待。我会马上自己编辑,但有些作者不喜欢这样

以上是关于初识SQL Server代理&作业的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server代理(8/12):使用SQL Server代理外部程序

SQL SERVER 2008复制数据库时发生执行SQL Server代理作业错误

SQL Server代理(11/12):维护计划作业

SQL Server代理(10/12):使用代理账号运行作业

从 VS 2012 在 SQL Server 2012 数据库项目中创建 SQL 代理作业

Sql Server代理作业