작업을 SQL Server 2005 서비스 팩 1을 설치한 후 토큰을 사용하여 작업 단계를 포함할 때 SQL Server 에이전트 작업이 실패합니다.

기술 자료 번역 기술 자료 번역
기술 자료: 915845 - 이 문서가 적용되는 제품 보기.
# 버그: 426808 (SQLBUDT)
모두 확대 | 모두 축소

현상

Microsoft SQL Server 2005 서비스 팩 1 (SP1)을 설치한 후 다음과 같은 문제가 발생할 수 있습니다.
  • 작업의 토큰을 사용하여 작업 단계를 포함할 때 SQL Server 에이전트 작업이 실패합니다.
  • 다음과 같은 오류 메시지가 나타납니다.
    하나 이상의 토큰을 작업 단계가 포함되어 있습니다. 작업을 실행하기 전에 SQL Server 2005 서비스 팩 1 또는 이후 버전을 사용하여 매크로를 토큰을 작업 단계는 모두 업데이트해야 합니다.
참고 이 문제는 빌드 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)]
예제에서는 다음과 같은 구문을 토큰 구문을 업데이트해야 합니다: 이전 SQL Server 2005 동작은 이스케이프 매크로가 필요하지 않은 것을
CREATE DATABASE [$(ESCAPE_RBRACKET(A-DBN))]
이 변경 다릅니다.

해결 방법

이 문제를 해결하려면 두 가지 모두 업데이트 또는 새 토큰 구문 토큰을 사용하여 특정 작업만. 이렇게 하려면 sp_AddEscapeNoneToJobStepTokens 저장 프로시저를 사용하십시오. 다음 Transact-SQL 스크립트를 사용하여 이 저장된 프로시저를 만들 수 있습니다.

참고 설치한 SQL Server 2005 SP1 빌드 2046 빌드 또는 나중에 빌드 있는지 확인하십시오. 또한 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 매크로를 추가합니다. 이 스크립트를 실행한 후 최대한 빨리 토큰을 사용하여 사용자의 작업 단계를 검토하는 것이 좋습니다. 그런 다음 ESCAPE_NONE 매크로 작업 단계는 컨텍스트에 대해 적합한 다른 이스케이프 매크로 중 하나를 바꿉니다.

참고 마스터 서버 (MSX) 및 대상 서버 (TSX) 환경에서 작업하는 경우 이 실행해야 하는 스크립트 MSX 및 해당 TSX 있는 TSX 마스터 작업을 올바르게 업데이트되었는지 확인합니다.

새 구문을 사용하도록 작업을 업데이트하는 방법, SQL Server 에이전트 작업 단계 토큰 교체 사용하려면 이스케이프 매크로를 사용하는 방법에 대한 자세한 내용은 SQL Server 2005 온라인 (2006년 4월) 또는 이후 버전의 SQL Server 2005 온라인 설명서의 "사용하여 토큰 있는 작업 단계 항목을 참조하십시오.

현재 상태

Microsoft는 "본 문서의 정보는 다음의 제품에 적용됩니다." 절에 나열된 Microsoft 제품에서 이 문제를 확인했습니다.

참조

작업 단계 토큰 사용하는 방법에 대한 자세한 내용은 다음 MSDN) Microsoft 개발자 네트워크 (웹 사이트를 방문하십시오.
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 KbMtko
기계 번역된 문서
중요: 본 문서는 전문 번역가가 번역한 것이 아니라 Microsoft 기계 번역 소프트웨어로 번역한 것입니다. Microsoft는 번역가가 번역한 문서 및 기계 번역된 문서를 모두 제공하므로 Microsoft 기술 자료에 있는 모든 문서를 한글로 접할 수 있습니다. 그러나 기계 번역 문서가 항상 완벽한 것은 아닙니다. 따라서 기계 번역 문서에는 마치 외국인이 한국어로 말할 때 실수를 하는 것처럼 어휘, 구문 또는 문법에 오류가 있을 수 있습니다. 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