在作业步骤中使用令牌

适用于:SQL ServerAzure SQL 托管实例

重要

Azure SQL 托管实例,目前大多数但并非所有 SQL Server 代理功能都受支持。 有关详细信息,请参阅 Azure SQL 托管实例 T-SQL 与 SQL Server 的差异 或 SQL 托管实例中的 SQL 代理作业限制

SQL Server 代理允许您在 Transact-SQL 作业步骤脚本中使用标记。 如果在编写作业步骤时使用标记,则可以为您提供编写软件程序时使用变量所提供的灵活性。 在您在作业步骤脚本中插入令牌之后,SQL Server 代理会在 Transact-SQL 子系统执行作业步骤之前的运行时替换该令牌。

了解标记用法

对 Windows 事件日志拥有写入权限的任何 Windows 用户都可以访问由 SQL Server 代理警报或 WMI 警报激活的作业步骤。 为了防范此安全隐患,默认情况下,可以在由警报激活的作业中使用的特定 SQL Server 代理标记已被禁用。 这些标记包括:A-DBN、、A-SVRA-SEVA-ERRA-MSGWMI(<property>)。 在此版本中,对标记的使用扩展至所有警报。

重要

如果您需要使用这些标记,请首先确保只有可信任的 Windows 安全组(如 Administrators 组)成员才对安装 SQL Server 的计算机的事件日志拥有写入权限。 然后在对象资源管理器中右键单击“SQL Server 代理”,选择“属性”,并在“警报系统”页上选择“为警报的所有作业响应替换标记”以启用这些标记。

SQL Server 代理标记替换简单且有效: SQL Server 代理以准确的文字字符串值替换标记。 所有标记都是区分大小写的。 您的作业步骤必须考虑到这一点,并正确地将使用的标记加上引号,或将替换字符串转换为正确的数据类型。

例如,您可以在作业步骤中使用以下语句输出数据库的名称:

PRINT N'Current database name is $(ESCAPE_SQUOTE(A-DBN))';

在此示例中,宏 ESCAPE_SQUOTE 使用 A-DBN 标记插入。 在运行时,令牌 A-DBN 将替换为相应的数据库名称。 转义宏将转义可能会在标记替换字符串中意外传递的任何单引号。 SQL Server 代理在最终字符串中将一个单引号替换为两个单引号。

例如,如果传递给替换令牌的字符串为 AdventureWorks2022'SELECT @@VERSION --,则由 SQL Server 代理作业步骤执行的命令为:

PRINT N'Current database name is AdventureWorks2022''SELECT @@VERSION --';

在这种情况下,不会执行已插入的语句 SELECT @@VERSION。 相反,多余的单引号会导致服务器将已插入语句作为字符串进行分析。 如果标记替换字符串不包含单引号,则不会转义任何字符,并且包含令牌的作业步骤按预期执行。

若要在作业步骤中调试标记使用,请使用 PRINT N'$(ESCAPE_SQUOTE(SQLDIR))' 之类的输出语句并将作业步骤输出保存到文件或表。 可以使用“作业步骤属性”对话框的“高级”页指定作业步骤输出文件或表。

SQL Server 代理标记和宏

下列各表列出并说明了 SQL Server 代理支持的标记和宏。

SQL Server 代理标记

令牌 DESCRIPTION
(A-DBN) 数据库名称 如果作业由某个警报运行,则在作业步骤中,数据库名称值将自动替换此标记。
(A-SVR) 服务器名称。 如果作业由某个警报运行,则在作业步骤中,服务器名称值将自动替换此标记。
(A-ERR) 错误号。 如果作业由某个警报运行,则在作业步骤中,错误号值将自动替换此标记。
(A-SEV) 错误严重性。 如果作业由某个警报运行,则在作业步骤中,错误严重性值将自动替换此标记。
(A-MSG) 消息文本。 如果作业由某个警报运行,则在作业步骤中,消息正文值将自动替换此标记。
(JOBNAME) 作业的名称。 此标记仅适用于 SQL Server 2016 及更高版本。
(STEPNAME) 步骤的名称。 此标记仅适用于 SQL Server 2016 及更高版本。
(DATE) 当前日期( yyyyMMdd 格式)。
(INST) 实例名称。 对于默认实例,此令牌具有默认实例名称: MSSQLSERVER
(JOBID) 职位 ID。
(MACH) 计算机名称。
(MSSA) 主 SQL ServerAgent 服务名称。
(OSCMD) 用于运行 CmdExec 作业步骤的程序的前缀。
(SQLDIR) 安装 SQL Server 的目录。 默认情况下,此值为 C:\Program Files\Microsoft SQL Server\MSSQL
(SQLLOGDIR) SQL Server 错误日志文件夹路径的替换令牌 - 例如 $(ESCAPE_SQUOTE(SQLLOGDIR))适用于:SQL Server 2014 (12.x) 及更高版本。
(STEPCT) 此步骤已执行的次数(不包括重试)。 步骤命令可以使用它来强制终止多步骤循环。
(STEPID) 步骤 ID。
(SRVR) 运行 SQL Server的计算机的名称。 如果 SQL Server 实例是命名实例,此值将包含实例名。
(TIME) 当前时间( HHmmss 格式)。
(STRTTM) 作业开始执行的时间( HHmmss 格式)。
(STRTDT) 作业开始执行的日期( yyyyMMdd 格式)。
(WMI(<property>)) 对于响应 WMI 警报而运行的作业,属性的值由<property>指定。 例如, $(WMI(DatabaseName)) 提供 DatabaseName 导致警报运行的 WMI 事件的属性值。

SQL Server 代理转义宏

转义宏 DESCRIPTION
$(ESCAPE_SQUOTE(<token_name>)) 转义标记替换字符串中的单引号 (')。 将一个单引号替换为两个单引号。
$(ESCAPE_DQUOTE(<token_name>)) 转义标记替换字符串中的双引号 (")。 将一个双引号替换为两个双引号。
$(ESCAPE_RBRACKET(<token_name>)) 转义标记替换字符串中的右方括号 (])。 将一个右方括号替换为两个右方括号。
$(ESCAPE_NONE(<token_name>)) 替换标记而不转义字符串中的任何字符。 提供此宏旨在支持在仅依赖受信任用户提供令牌替换字符串的环境中保持向后兼容性。 有关详细信息,请参阅下一部分, 更新作业步骤以使用宏

更新作业步骤以使用宏

下表说明 SQL Server 代理如何处理标记替换。 若要启用或禁用警报标记替换,请在对象资源管理器中右键单击“SQL Server 代理”,选择“属性”,然后在“警报系统”页上选中或清除“为警报的所有作业响应替换标记”复选框。

标记语法 启用警报标记替换 禁用警报标记替换
已使用 ESCAPE 成功替换作业中的所有标记。 不替换由警报激活的标记。 这些令牌包括A-DBNA-SVRA-ERRA-SEVA-MSGWMI(<property>)。 成功替换其他静态标记。
未使用转义宏 任何包含标记的作业均失败。 任何包含标记的作业均失败。

标记语法更新示例

下面是标记语法示例,可帮助说明如何使用这些命令。

答: 在非嵌套字符串中使用标记

以下示例演示如何使用适当的转义宏更新基本非嵌套脚本。 在运行更新脚本之前,以下作业步骤脚本使用作业步骤标记输出相应的数据库名称:

PRINT N'Current database name is $(A-DBN)';

在运行更新脚本之后,ESCAPE_NONE 宏会在 A-DBN 标记之前插入。 由于使用单引号分隔输出字符串,因此必须通过插入 ESCAPE_SQUOTE 宏来更新作业步骤,如下所示:

PRINT N'Current database name is $(ESCAPE_SQUOTE(A-DBN))';

B. 在嵌套字符串中使用标记

对于在嵌套字符串或语句中使用标记的作业步骤脚本,应将嵌套语句重写为多个语句,然后插入相应的转义宏。

例如,请考虑以下作业步骤,该步骤使用 A-MSG 标记,并且尚未使用转义宏进行更新:

PRINT N'Print ''$(A-MSG)''';

在运行更新脚本之后,会插入带有标记的 ESCAPE_NONE 宏。 但在这种情况下,必须按如下方式在不使用嵌套的情况下重写脚本,并插入 ESCAPE_SQUOTE 宏以正确转义可能在标记替换字符串中传递的分隔符:

DECLARE @msgString AS NVARCHAR (MAX);
SET @msgString = '$(ESCAPE_SQUOTE(A-MSG))';
SET @msgString = QUOTENAME(@msgString, '''');
PRINT N'Print ' + @msgString;

在此示例中,该 QUOTENAME 函数设置引号字符。

C. 将标记用于 ESCAPE_NONE 宏

以下示例是一个脚本的一部分,该脚本从 sysjobs 表检索job_id,并使用JOBID令牌填充在脚本中之前声明为二进制数据类型的@JobID变量。

注释

由于二进制数据类型不需要分隔符,因此 ESCAPE_NONE 宏与 JOBID 令牌一起使用。 运行更新脚本之后,无需更新此作业步骤。

DECLARE @JobID UNIQUEIDENTIFIER;
SET @JobID = $(ESCAPE_NONE(JOBID));

这直接将JOBID令牌中的值分配给@JobID,消除不必要的数据库查询和隐式转换,从而解决了 C 节中提出的问题。