动态查询语法错误

时间:2016-06-08 06:16:35

标签: sql sql-server tsql stored-procedures dynamic-sql

我正在尝试在我们的SQL环境中更改存储过程,该环境用于远程创建备份作业。以下是程序

/****** Object:  StoredProcedure [dbo].[CreateBackupJobGroupBFull]    Script Date: 8/06/2016 3:18:25 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


ALTER PROCEDURE [dbo].[CreateBackupJobGroupAFull]   
(@servername nvarchar(100))  
AS  

declare @commandstr nvarchar(max)
DECLARE @SQL nvarchar(4000)
DECLARE @TableExists bit
DECLARE @recp nvarchar(100)
DECLARE @error_msg varchar(300)
declare @groupAfull nvarchar(150)
declare @schedulestr nvarchar(150)
DECLARE @pripathstr VARCHAR(1256)
DECLARE @secpathstr VARCHAR(1256)
declare @defaultscheduleStart varchar(30)
declare @gstr varchar(5)
declare @g varchar(1)

set @groupAfull = 'DBABackupJobGroupA_Full'
set @recp = '**************'
set @defaultscheduleStart = '230000'
set @gstr = '_GA_F'
set @g = 'A'

SET @SQL = 'SELECT @TableExists = CASE WHEN TableExists = 0 THEN 0 ELSE 1 END
            FROM OPENQUERY(' + QUOTENAME(@servername) 
            + ', ''SELECT TableExists = COUNT(*) 
                    from msdb.dbo.sysjobs
                    WHERE name = '''' + @groupAfull + '''''');';

begin try
                        EXECUTE sp_executesql @SQL, N'@TableExists BIT OUTPUT', @TableExists OUT;

END TRY
BEGIN CATCH
                        print 'error message is:'
                        print error_message()
                        set @error_msg='Error occurred when executing CreatBackupJob against server ' + @ServerName + '. Failed to check control table locally. The error message is: ' + error_message()

                        EXEC msdb.dbo.sp_send_dbmail
                        @profile_name = 'Internal mail',
                        @recipients = @recp,
                        @body = @error_msg ,
                        @subject = 'Error occurred on reporting server' ;
END CATCH                       

IF (@TableExists = 0 )
BEGIN

            --add new job for the instance
            set @sql = 'if not exists (select * from [' + @ServerName + '].msdb.dbo.sysjobs where name = ''' + @groupAfull + ''')
            BEGIN
            EXEC [' + @ServerName + '].msdb.dbo.sp_add_job
            @job_name = ''' + @groupAfull + ''', 
            @enabled = 1,
            @description = N''DBA backup job. DO NOT REMOVE. V1.5 (2015-06-16)'',
            @owner_login_name = N''sa''
            EXEC [' + @ServerName + '].msdb.dbo.sp_add_jobserver
            @job_name = ''' + @groupAfull + ''', 
            @server_name = ''(local)''
            end
            '

            begin try
                        EXEC sp_executeSQL @sql

            END TRY
            BEGIN CATCH
                        print 'error message is:'
                        print error_message()
                        set @error_msg='Error occurred when executing CreatBackupJob against server ' + @ServerName + '. Failed to create new job'+ @groupAfull +'. The error message is: ' + error_message()

                        EXEC msdb.dbo.sp_send_dbmail
                        @profile_name = 'Internal mail',
                        @recipients = @recp,
                        @body = @error_msg ,
                        @subject = 'Error occurred on reporting server' ;
            END CATCH       

SET @pripathstr = (SELECT Top 1 [PrimDestinationfull] FROM [SQLADMIN].[dbo].[BackupDestination])
SET @secpathstr = (SELECT Top 1 [SecDestinationfull] FROM [SQLADMIN].[dbo].[BackupDestination])

            --add job step 1 'test'
            set @commandstr = '
DECLARE @dbname VARCHAR(150) -- database name  
DECLARE @pripath VARCHAR(1256) -- path for backup files  
DECLARE @secpath VARCHAR(1256) -- path for backup files  
DECLARE @path VARCHAR(1256) -- path for backup files  
DECLARE @fileName VARCHAR(1256) -- filename for backup  
DECLARE @fileDate VARCHAR(40) -- used for file name
DECLARE @DBGROUP varchar(1)
DECLARE @filecount int
DECLARE @lastbackuptime datetime

SET @DBGROUP = ''''' + @g + '''''

IF not EXISTS (SELECT [name] FROM msdb.sys.tables WHERE  [name] = ''''DBBackupControlTbl''''  )  
    begin   
        raiserror(''''Control table not found!'''', 20, -1) with log
        return 
    end 
else

    if (select count(*) from msdb.dbo.DBBackupControlTbl where BackupGroup = @DBGROUP) > 0 

    -- specify database backup directory
    SET @pripath =  ''''' + @pripathstr + '''''
    SET @secpath =  ''''' + @secpathstr + '''''


    if object_id(''''tempdb.dbo.#fileExist'''') is not null
          drop table #fileExist
    create table #fileExist (
          fileExists int,
          fileIsDir int,
          parentDirExists int
          )
    insert into #fileExist
    exec xp_fileexist @pripath

    if object_id(''''tempdb.dbo.#fileExist2'''') is not null
          drop table #fileExist2
    create table #fileExist2 (
          fileExists int,
          fileIsDir int,
          parentDirExists int
          )
    insert into #fileExist2
    exec xp_fileexist @secpath

    if (select count(*) from #fileExist where fileIsDir = 1) > 0 
        begin
        set @path = @pripath
        end
    ELSE 
        begin
            if (select count(*) from #fileExist2 where fileIsDir = 1) > 0
            begin
                set @path = @secpath
            end
        ELSE
            begin
                raiserror(''''None of the backup directory can be accessed at the moment!'''', 20, -1) with log
                return 
            end
        end

    set @filecount = 999
    while @filecount >= 15
    begin
        IF OBJECT_ID(''''tempdb..#DirectoryTree'''') IS NOT NULL
              DROP TABLE #DirectoryTree;

        CREATE TABLE #DirectoryTree (
               id int IDENTITY(1,1)
              ,subdirectory nvarchar(512)
              ,depth int
              ,isfile bit);

        INSERT #DirectoryTree (subdirectory,depth,isfile)
        EXEC master.sys.xp_dirtree @path,1,1;

        select @filecount = COUNT(*) FROM #DirectoryTree
        WHERE isfile = 1 AND RIGHT(subdirectory,4) = ''''.sts''''

        if @filecount >= 15
            begin
            print ''''Wait for status file to be cleared.''''
            waitfor delay ''''00:00:10''''
            end
        else
            begin
            print ''''Backing up now.''''

            DECLARE @statusfile AS VARCHAR(150)
            DECLARE @cmd AS VARCHAR(150)



    -- specify filename format
    SELECT @fileDate = replace(replace(CONVERT(VARCHAR(40),GETDATE(),120), '''':'''', ''''-'''' ), '''' '''', ''''T'''')

    set @statusfile = @path + replace(@@SERVERNAME, ''''\'''', ''''-'''')  + ''''_'''' + @fileDate + '''''+ @gstr + ''''' +  ''''.STS''''
    set @cmd = ''''echo '''' +  @filedate + '''' > '''' + @statusfile
    EXECUTE Master.dbo.xp_CmdShell  @Cmd


    DECLARE db_cursor CURSOR FOR  
    select databasename from msdb.dbo.DBBackupControlTbl where BackupGroup = @DBGROUP

    OPEN db_cursor   
    FETCH NEXT FROM db_cursor INTO @dbname   

    WHILE @@FETCH_STATUS = 0 
        BEGIN   
       SET @fileName = @path + replace(@@SERVERNAME, ''''\'''', ''''-'''') + ''''_'''' + @dbname + ''''_'''' + @fileDate + '''''+ @gstr + ''''' +  ''''.BAK''''  
       BACKUP DATABASE @dbname TO DISK = @fileName  
       FETCH NEXT FROM db_cursor INTO @dbname   
END 

    CLOSE db_cursor   
    DEALLOCATE db_cursor

    set @cmd = ''''del '''' + @statusfile
    EXECUTE Master.dbo.xp_CmdShell  @Cmd

    end
    continue
    end
'
            print @commandstr

            set @sql = 'if not exists (select * from [' + @ServerName + '].msdb.dbo.sysjobsteps where step_name = ''' + @groupAfull + ''')
            BEGIN 
            EXEC [' + @ServerName + '].msdb.dbo.sp_add_jobstep
            @job_name = ''' + @groupAfull + ''', 
            @step_name = ''' + @groupAfull + ''',
            @subsystem = N''TSQL'',
            @command = N''' + @commandstr + ''',
            @retry_attempts = 3,
            @retry_interval = 1,
            @on_success_action = 1 ,
            @on_fail_action= 2;
            end 
            '
            begin try
                        EXEC sp_executeSQL @sql

            END TRY
            BEGIN CATCH
                        print 'error message is:'
                        print error_message()
                        set @error_msg='Error occurred when executing CreatBackupJob against server ' + @ServerName + '. Failed to create new job step '+ @groupAfull +'. The error message is: ' + error_message()

                        EXEC msdb.dbo.sp_send_dbmail
                        @profile_name = 'Internal mail',
                        @recipients = @recp,
                        @body = @error_msg ,
                        @subject = 'Error occurred on reporting server' ;
            END CATCH       

            --add the schedule
            set @schedulestr = @groupAfull + '_Schedule'
            set @sql = 'if not exists (select * from [' + @ServerName + '].msdb.dbo.sysschedules where name = ''' + @schedulestr + ''')
            BEGIN
            EXEC [' + @ServerName + '].msdb.dbo.sp_add_schedule
            @schedule_name = ''' + @schedulestr + ''', 
            @enabled=1, 
            @freq_type=4, 
            @freq_interval=1, 
            @freq_subday_type=1, 
            @freq_subday_interval=0, 
            @freq_relative_interval=0, 
            @freq_recurrence_factor=1, 
            @active_start_date=20140819, 
            @active_end_date=99991231, 
            @active_start_time= '+ @defaultscheduleStart +', 
            @active_end_time=235959
            end
            '
            --print @sql
            begin try
                        EXEC sp_executeSQL @sql

            END TRY
            BEGIN CATCH
                        print 'error message is:'
                        print error_message()
                        set @error_msg='Error occurred when executing CreatBackupJob against server ' + @ServerName + '. Failed to create new job schedule '+ @groupAfull +'. The error message is: ' + error_message()

                        EXEC msdb.dbo.sp_send_dbmail
                        @profile_name = 'Internal mail',
                        @recipients = @recp,
                        @body = @error_msg ,
                        @subject = 'Error occurred on reporting server' ;
            END CATCH       

            --attach the schedule
            set @sql = 'EXEC [' + @ServerName + '].msdb.dbo.sp_attach_schedule
            @job_name = ''' + @groupAfull + ''',
            @schedule_name = ''' + @schedulestr + '''   
            '
            begin try
                        EXEC sp_executeSQL @sql

            END TRY
            BEGIN CATCH
                        print 'error message is:'
                        print error_message()
                        set @error_msg='Error occurred when executing CreatBackupJob against server ' + @ServerName + '. Failed to attach the schedule to the job '+ @groupAfull +'. The error message is: ' + error_message()

                        EXEC msdb.dbo.sp_send_dbmail
                        @profile_name = 'Internal mail',
                        @recipients = @recp,
                        @body = @error_msg ,
                        @subject = 'Error occurred on reporting server' ;
            END CATCH       
end 
GO

创建的作业是通过3次重试创建的,因此如果备份失败,它将退休,然后再次备份所有数据库,无论它在哪里失败。我正在尝试更改过程,以便在作业失败时为失败的数据库运行备份。我写了下面的代码来替换备份数据库的地方。

*@lastbackuptime  is declared 

BEGIN
    SET @lastbackuptime = (select max(backup_finish_date) from msdb.dbo.backupset where database_name=@dbname and type=''''D'''')
    if (Select datediff(hour,@lastbackuptime,getdate()))> 18
        Begin
            SET @fileName = @path + replace(@@SERVERNAME, ''''\'''', ''''-'''') + ''''_'''' + @dbname + ''''_'''' + @fileDate + '''''+ @gstr + ''''' +  ''''.BAK''''  
            BACKUP DATABASE @dbname TO DISK = @fileName   
        End
    else
        Begin
        Print ''''Backup exists for database ''''
        End  
    FETCH NEXT FROM db_cursor INTO @dbname   
END 

当我从作业步骤进行此更改时,查询解析正常并且作业运行正常。但是,当我把它放在这个过程中并尝试远程创建作业时,它会因语法错误而失败。因为它是动态sql,所以它没有提供任何线索在哪里是语法错误。

如果有人能发现它会很棒。

此致 SID

1 个答案:

答案 0 :(得分:1)

"动态SQL无效的问题"类型经常出现,有时(如你的情况)很难回答,因为我们无法执行你提供的代码。

所以这个答案描述了调试动态SQL的方法。

  1. 添加PRINT语句,以便在执行之前打印动态SQL字符串。使用PRINT命令时,可以截断长字符串,因此需要使用变通方法(How to print VARCHAR(MAX) using Print Statement?提供了一些解决方案)。

  2. 打印完整个字符串后,请检查语法错误和/或运行它。您可以通过将代码粘贴到新的查询窗口并运行" Parse"来检查代码的语法错误。 (按Ctrl + F5)。

  3. 识别出动态SQL中的错误后,您需要在动态SQL生成器代码中找到产生此错误的位置并将其修复。

  4. 重复此过程,直到没有错误。