SQL Server 代理程式作業失敗時工作包含安裝 SQL Server 2005 Service Pack 1 之後,使用語彙基元 (Token) 的作業步驟

文章翻譯 文章翻譯
文章編號: 915845 - 檢視此文章適用的產品。
Bug #: 426808 (SQLBUDT)
全部展開 | 全部摺疊

徵狀

安裝 Microsoft SQL Server 2005 Service Pack 1 (SP1) 之後,您遇到下列行為:
  • SQL Server 代理程式作業失敗時工作包含使用語彙基元 (Token) 的作業步驟。
  • 您會收到下列錯誤訊息:
    作業步驟包含一或多個語彙基元 (Token)。SQL Server 2005 Service Pack 1 或更新版本,與語彙基元 (Token) 的所有作業步驟必須都更新與巨集才可以執行工作。
附註組建 2046年 (含) 以後版本的 SQL Server 2005,就會發生這個問題。

發生的原因

在 SQL Server 2005 SP1 中已有變更 SQL Server 代理程式作業步驟的語彙基元語法。現在,您必須在作業步驟中包含未逸出巨集與使用的所有語彙基元。如果不包含逸出巨集將會失敗,這些作業步驟。下表列出逸出巨集。
摺疊此表格展開此表格
逸出巨集描述
$ (ESCAPE_SQUOTE (TokenName))此巨集逸出語彙基元的取代字串中的單引號 (')。巨集以兩個單引號取代一個所有格符號。
$ (ESCAPE_DQUOTE (TokenName))此巨集逸出引號 (' ') 語彙基元的取代字串中。巨集以兩個引號取代一個引號。
$ (ESCAPE_RBRACKET (TokenName))此巨集逸出語彙基元的取代字串中的右括弧 (])。巨集來取代兩個右括號一個右括號。
$ (ESCAPE_NONE (TokenName))巨集將不需要逸出字串中的任何字元取代語彙基元。此巨集是為了支援其中語彙基元的取代字串只預期從受信任的使用者的環境中的回溯相容性而提供。
作業步驟,例如可能包含下列使用 A DBN 語彙基元的 Transact-SQL 陳述式: 在此範例
CREATE DATABASE [$(A-DBN)]
,您必須更新語彙基元的語法到下列語法:
CREATE DATABASE [$(ESCAPE_RBRACKET(A-DBN))]
這項變更不同於之前的 SQL Server 2005 行為位置逸出的巨集並不需要。

解決方案

如果要解決這個問題,更新任一全部或只有特定工作使用語彙基元 (Token) 至新的語彙基元語法。若要執行這項操作,請使用 sp_AddEscapeNoneToJobStepTokens 預存程序]。您可以使用下列的 Transact-SQL 指令碼來建立這個預存程序。

附註請確定您安裝 SQL Server 2005 SP1 建置組建 2046年或較新的組建。此外,您必須是系統管理員 (sysadmin) 固定的伺服器角色的成員,才能執行 script.
      -- This script is used to automatically edit SQL Agent job steps so that
-- unescaped tokens are prefaced with the ESCAPE_NONE syntax that was added in
-- SQL Server 2005 SP1.

if (@@microsoftversion < 0x90007FE)
BEGIN
    RAISERROR('This script should only be run on at least SQL Server 2005 SP1.', 20, 127) WITH LOG
    return
END

use msdb
go

if exists (select * from sys.objects where name = N'fn_PrefaceTokensWithEscapeNone' and type = 'FN')
    drop function fn_PrefaceTokensWithEscapeNone
go

-- This function manipulates @commands so that all bare tokens
-- are prefaced with ESCAPE_NONE.
create function fn_PrefaceTokensWithEscapeNone(@commands nvarchar(max)) RETURNS nvarchar(max)
AS
BEGIN
    if (@commands IS NULL)
    BEGIN
        return @commands
    END

    -- In order to let this script run under SQLCMD mode, we define
    -- the special "$(" variable start string by concatenation so that
    -- sqlcmd mode does not think that we are defining one of its variables.
    declare @strVariableStart nchar(2)
    select @strVariableStart = N'$' + N'('

    declare @idxTokenStart int
    select @idxTokenStart = CHARINDEX(@strVariableStart, @commands)
    while (@idxTokenStart != 0 and @idxTokenStart is not null)
    BEGIN
        declare @idxCloseParen int
        select @idxCloseParen = CHARINDEX(N')', SUBSTRING(@commands, @idxTokenStart, LEN(@commands)))
        -- Error checking. If there is no close parenthesis, return.
        if (0 = @idxCloseParen)
        BEGIN
            return @commands
        END

        -- Deduce the token variable.
        declare @tokenLen int
        select @tokenLen = @idxCloseParen - LEN(@strVariableStart) - 1
        declare @token nvarchar(max)
        select @token = SUBSTRING(@commands, @idxTokenStart + LEN(@strVariableStart), @tokenLen)

        -- Verify if @token contains a mis-matched number of open and
        -- close parens. This behavior could happen if invalid syntax is
        -- in a comment block. If so, skip to the next token.
        declare @idx int
        declare @cOpenParens int
        declare @cCloseParens int

        select @cOpenParens = 0
        select @idx = CHARINDEX(N'(', @token);
        while (@idx != 0)
        BEGIN
            select @cOpenParens = @cOpenParens + 1
            select @idx = CHARINDEX(N'(', @token, @idx + 1);
        END

        select @cCloseParens = 0
        select @idx = CHARINDEX(N')', @token);
        while (@idx != 0)
        BEGIN
            select @cCloseParens = @cCloseParens + 1
            select @idx = CHARINDEX(N')', @token, @idx + 1);
        END

        -- Special case for the WMI token.
        if (N'WMI(' = SUBSTRING(@token, 1, LEN(N'WMI(')))
        BEGIN
                select @cOpenParens = @cOpenParens - 1
        END

        if ((@cOpenParens = @cCloseParens) and
            (N'ESCAPE_NONE(' != SUBSTRING(@token, 1, LEN(N'ESCAPE_NONE('))) and
            (N'ESCAPE_SQUOTE(' != SUBSTRING(@token, 1, LEN(N'ESCAPE_SQUOTE('))) and
            (N'ESCAPE_DQUOTE(' != SUBSTRING(@token, 1, LEN(N'ESCAPE_DQUOTE('))) and
            (N'ESCAPE_RBRACKET(' != SUBSTRING(@token, 1, LEN(N'ESCAPE_RBRACKET('))))
        BEGIN
            select @commands = STUFF(@commands, @idxTokenStart + LEN(@strVariableStart), @tokenLen, N'ESCAPE_NONE(' + @token + N')')
        END
        select @idxTokenStart = CHARINDEX(@strVariableStart, @commands, @idxTokenStart + 1)
    END

    return @commands

END
go

if exists (select * from sys.objects where name = N'sp_AddEscapeNoneToJobStepTokens' and type = 'P')
    drop procedure sp_AddEscapeNoneToJobStepTokens
go

-- This procedure allows you to update jobs so that bare tokens
-- are prefaced with ESCAPE_NONE. By default, all jobs are updated.
-- You can optionally specify @job_name, @job_id, or @owner_name
-- to limit the jobs that will be affected.
CREATE PROCEDURE sp_AddEscapeNoneToJobStepTokens
(
    @job_name nvarchar(128) = null,
    @job_id uniqueidentifier = null,
    @owner_name nvarchar(256) = null
)
AS


  -- Find the jobs to update. These jobs must match all of the input
  -- criteria, unless all of the inputs are null. In this case, 
  -- examine all jobs.  The jobs must also be jobs created locally,
  -- such as sysjobs.originating_server_id = 0. These jobs should not be a job that we run
  -- because another server told us to.  Furthermore, if the job
  -- is local but it is meant to be run on a target server, we send an
  -- update for the job.
  declare @jobsToUpdate TABLE (job_id uniqueidentifier not null)
  
  insert into @jobsToUpdate
      select job_id
      from sysjobs
      where originating_server_id = 0 -- local jobs
      and ((COALESCE(@job_name, sysjobs.name) = sysjobs.name) and
           (COALESCE(@job_id, sysjobs.job_id) = sysjobs.job_id) and
           (COALESCE(@owner_name, suser_sname(sysjobs.owner_sid)) = suser_sname(sysjobs.owner_sid)))
  
  -- Now find the job steps to update, creating the new command by using
  -- fn_PrefaceTokensWithEscapeNone.
  declare @jobStepsToUpdate TABLE (job_id uniqueidentifier not null, 
                                   step_id int not null, 
                                   command_old nvarchar(max) null, 
                                   command_new nvarchar(max) null,
                                   output_file_old nvarchar(max) null, 
                                   output_file_new nvarchar(max) null)
  
  insert into @jobStepsToUpdate
  (job_id, step_id, command_old, command_new, output_file_old, output_file_new)
      select job_id, step_id, command, dbo.fn_PrefaceTokensWithEscapeNone(command), output_file_name, dbo.fn_PrefaceTokensWithEscapeNone(output_file_name)
      from sysjobsteps
      where sysjobsteps.job_id = 
      (select job_id 
       from @jobsToUpdate 
       where job_id = sysjobsteps.job_id)
  
  -- Now we update the actual job step commands. We do this first before
  -- we push out the updated jobs to the target servers so the
  -- target servers actually get the updated version.
  declare @updated_job_id uniqueidentifier
  declare @updated_job_step_id int
  declare @updated_job_step_command nvarchar(max)
  declare @updated_job_step_output_file nvarchar(max)
  
  declare job_steps_cursor CURSOR FOR
      select job_id, step_id, command_new, output_file_new
      from @jobStepsToUpdate
      order by job_id, step_id
  
  OPEN job_steps_cursor
  FETCH NEXT from job_steps_cursor into @updated_job_id, @updated_job_step_id, @updated_job_step_command, @updated_job_step_output_file
  WHILE (@@FETCH_STATUS <> -1)
  BEGIN
      IF (@@FETCH_STATUS <> -2)
      BEGIN
          EXEC sp_update_jobstep @job_id = @updated_job_id, @step_id = @updated_job_step_id, @command = @updated_job_step_command, @output_file_name = @updated_job_step_output_file
      END
      FETCH NEXT from job_steps_cursor into @updated_job_id, @updated_job_step_id, @updated_job_step_command, @updated_job_step_output_file
  END
  
  CLOSE job_steps_cursor
  DEALLOCATE job_steps_cursor
  
  
  -- For multiserver jobs, call the sp_post_msx_operation stored procedure to update
  -- all the target servers. Note that the sp_post_msx_operation stored procedure is safe
  -- to call because it verifies whether the job is really a multiserver job.
  declare jobs_cursor CURSOR FOR
      select job_id
      from @jobsToUpdate
  
  OPEN jobs_cursor
  FETCH NEXT from jobs_cursor into @updated_job_id
  WHILE (@@FETCH_STATUS <> -1)
  BEGIN
      IF (@@FETCH_STATUS <> -2)
      BEGIN
          EXEC sp_post_msx_operation @operation = 'UPDATE', @job_id = @updated_job_id
      END
      FETCH NEXT from jobs_cursor into @updated_job_id
  END
  
  CLOSE jobs_cursor
  DEALLOCATE jobs_cursor

  -- List the jobs that we ran on, including the previous command
  -- text. We list all of the job steps, even the ones that we did not
  -- update. Otherwise, a jumble of job steps from
  -- different jobs run together and the output is not
  -- useful.
  select N'Warning - Jobs Updated' = N'The following job steps and job output file names were analyzed and potentially updated to add the ESCAPE_NONE macro before any job tokens that were not already escaped. Please review the modified job steps and replace ESCAPE_NONE with the correct escape macro.'
  
  select suser_sname(jobs.owner_sid) as N'Job owner', 
         jobs.name as N'Job name', 
         jobs.job_id, 
         jobStepsUpdated.step_id, 
         N'Modified' = CASE WHEN jobStepsUpdated.command_new != jobStepsUpdated.command_old or jobStepsUpdated.output_file_new != jobStepsUpdated.output_file_old THEN 1 ELSE 0 END,
         N'Command' = jobStepsUpdated.command_new, 
         N'Previous Command' = jobStepsUpdated.command_old, 
         N'Output file' = jobStepsUpdated.output_file_new, 
         N'Previous Output file' = jobStepsUpdated.output_file_old
      from sysjobs as jobs, @jobsToUpdate as jobsUpdated, @jobStepsToUpdate as jobStepsUpdated
      where jobsUpdated.job_id = jobs.job_id and jobsUpdated.job_id = jobStepsUpdated.job_id
      order by 'Job name', jobStepsUpdated.step_id
go

執行指令碼之後會建立 sp_AddEscapeNoneToJobStepTokens 預存程序。預設情況下,將會更新所有的作業,如果您執行此預存程序不加任何參數。如果想更新只有特定的工作,您必須指定至少一個下列三個參數的非 Null 值:
  • @ job_name
  • @ job_id
  • @ owner_name
比方說,您可以使用下列語法:
  • 更新所有的作業:
    EXEC sp_AddEscapeNoneToJobStepTokens
  • 藉由指定作業名稱更新一個作業:
    EXEC sp_AddEscapeNoneToJobStepTokens 'MyJob'
  • 更新相同的擁有者所擁有的作業:
    EXEC sp_AddEscapeNoneToJobStepTokens null,null,'JobOwner'
此指令碼會將 ESCAPE_NONE 巨集加入至包含語彙基元 (Token) 的所有作業步驟。執行此指令碼之後我們建議您檢閱您工作的步驟儘速使用語彙基元 (Token)。然後,使用其中一個其他逸出的巨集適用於工作步驟內容取代 ESCAPE_NONE 巨集。

附註如果您使用在主要伺服器 (MSX) 和目標伺服器 (TSX) 環境中您必須執行這個指令碼在 MSX 和 [TSX 請確定 [TSX 上的主要工作正確更新。

如需有關如何更新使用新的語法和如何使用逸出的巨集來啟用 SQL Server 代理程式作業步驟中的語彙基元取代工作請參閱 < 使用語彙基元中工作步驟 > 主題在 SQL Server 2005 線上叢書 》 (2006 年四月) 或更新版本的 SQL Server 2005 線上叢書 》 中。

狀況說明

Microsoft 已確認<適用於>一節所列之 Microsoft 產品確實有此問題。

?考

如需有關如何使用作業步驟中的語彙基元 (Token) 的詳細資訊,請造訪下列 Microsoft 開發 o 人 h 員 ? 工 u 具 ? 網路 (MSDN) 網站]:
http://msdn2.microsoft.com/en-us/library/ms175575.aspx

屬性

文章編號: 915845 - 上次校閱: 2006年5月24日 - 版次: 2.1
這篇文章中的資訊適用於:
  • Microsoft SQL Server 2005 Standard Edition
  • Microsoft SQL Server 2005 Workgroup Edition
  • Microsoft SQL Server 2005 Enterprise Edition
關鍵字:?
kbmt kbsql2005presp1fix kbprb kbexpertiseadvanced kbtshoot KB915845 KbMtzh
機器翻譯
重要:本文是以 Microsoft 機器翻譯軟體翻譯而成,而非使用人工翻譯而成。Microsoft 同時提供使用者人工翻譯及機器翻譯兩個版本的文章,讓使用者可以依其使用語言使用知識庫中的所有文章。但是,機器翻譯的文章可能不盡完美。這些文章中也可能出現拼字、語意或文法上的錯誤,就像外國人在使用本國語言時可能發生的錯誤。Microsoft 不為內容的翻譯錯誤或客戶對該內容的使用所產生的任何錯誤或損害負責。Microsoft也同時將不斷地就機器翻譯軟體進行更新。
按一下這裡查看此文章的英文版本:915845
Microsoft及(或)其供應商不就任何在本伺服器上發表的文字資料及其相關圖表資訊的恰當性作任何承諾。所有文字資料及其相關圖表均以「現狀」供應,不負任何擔保責任。Microsoft及(或)其供應商謹此聲明,不負任何對與此資訊有關之擔保責任,包括關於適售性、適用於某一特定用途、權利或不侵權的明示或默示擔保責任。Microsoft及(或)其供應商無論如何不對因或與使用本伺服器上資訊或與資訊的實行有關而引起的契約、過失或其他侵權行為之訴訟中的特別的、間接的、衍生性的損害或任何因使用而喪失所導致的之損害、資料或利潤負任何責任。

提供意見

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com