在个人数据库实例中,有不少相似下图所示的数据库,这些数据库的名称是有规律的,每一个数据库包含的表都是相同的,其中2个表是相似流水记录的表,表的数据量会比较大,占用的空间有几十G到上百G不等,这2个表相对于其它的配置表来讲是比较不重要的。html
如今有一个需求就是对数据库进行备份,容许丢失这两个表的数据,保留重要的配置表数据,你是否遇到过一样的问题呢?这个时候你会怎么作呢?你有什么方案呢?有什么方法能够快速备份这些数据库呢?sql
(Figure1:数据库列表)shell
阅读本文以前你能够先参考:SQL Server 批量完整备份数据库
经过上面的描述,其中很重要的一点就是每一个数据库中有2个大表,并且这些数据是不重要的,那么咱们对这2个大表作表分区,把大数据放到其它文件组中,只留重要的配置表在主文件组(PRIMARY)中,接着就能够对主文件组进行备份,这样既知足了备份重要表数据,并且不会形成备份文件过大、占用磁盘空间、备份时间过长等问题。测试
使用维护计划;请参考:SQL Server 维护计划备份主分区,只要在【执行T-SQL语句】的任务中使用循环递归全部数据库进行备份,在【清除历史记录】任务和【清除维护】任务中选择父目录就能够了;ui
方案一中经过维护计划生成的做业是看不到具体的备份代码的,因此方案二就是为了补充方案一的。若是你没有做业执行时间的特殊要求,你能够建立一个做业,循环数据库名称进行主分区的备份,只是把维护计划【执行T-SQL语句】的内容放到了做业中;3d
这个方案有如下几个缺点:代理
a) 没有办法单独控制某个数据库备份的时间;
b) 数据库一多的话,在Job的Message里面没有办法显示那么多的信息;
c) 备份串行执行的,没有办法进行并行备份;
d) 个个数据库的备份不是单独的,若是做业出错,会形成后面的数据库没法备份;
e) 貌似是先写入内存,等做业完成后才一次性写入硬盘的?当数据库备份文件比较大的时候会莫名只备份几个数据库做业就退出了,没有查明是什么缘由;
具体的操做步骤能够参考:SQL Server 批量主分区备份(One Job),这个方案最终的做业形式为:
(Figure2:做业列表)
方案三是为了控制备份的时间而准备的,就是每一个数据库都建立一个做业,这样的好处是能够充分的控制到每一个数据库,由于咱们能够须要对某个数据库进行完整备份,并且备份的粒度也有可能不一样(重要客户的备份粒度会更小)
(Figure3:做业列表)
既然咱们选择了方案三,那咱们如何快速(批量)建立这些做业呢?咱们先以数据库Barefoot.Opinion.9197为例,建立出一个做业的模板后,经过修改、替换这个做业的代码来实现批量生成新的可执行的做业脚本;
经过下面的几个步骤你就能够批量的生成备份做业:
1) 备份主分区完整备份的SQL代码(备份配置表);备份主分区差别备份的SQL代码(减轻备份文件空间压力)
2) 自动删除备份文件的SQL代码;(保证磁盘有足够空间)
3) 批量建立文件夹;(每一个数据库单独一个文件夹)
4) 批量建立做业方案;(每一个做业的名称、目录都不相同)
5) 批量修改做业计划的时间;(均衡分配做业的执行时间)
6) 批量删除做业;(方便维护)
7) 查看做业的执行状况;(防止做业时间过长,过长能够考虑预警)
(一) 下面的代码实现了主分区完整备份和主分区差别备份,当是星期一的深夜的时候,咱们作完整备份,若是是其它时候咱们就作差别备份,具体是何时,这个就经过计划里面的时候来控制了(计划的执行时间为星期1、星期3、星期五,这就表明星期一深夜作了完整备份、星期三和星期五分别作了差别备份)。
生成的备份文件名为:(这样的文件名方便阅读;并且能精确到秒,重复的概率不大)
DBName _Primary_Full_2013_01_14_002007.bak
DBName_Primary_Diff_2013_01_16_002034.bak
--1设置完整模式 USE [master] GO ALTER DATABASE [DBName.9197] SET RECOVERY FULL WITH NO_WAIT GO --2备份主分区 DECLARE @CurrentTime VARCHAR(50), @FileName VARCHAR(200) SET @CurrentTime = REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120 ),'-','_'),' ','_'),':','') IF(DATEPART(DW, GETDATE()) = 2)--星期一 BEGIN --完整备份 SET @FileName = 'E:\DBBackup\DBName.9197\DBName.9197_Primary_Full_' + @CurrentTime+'.bak' BACKUP DATABASE [DBName.9197] FILEGROUP='PRIMARY' TO DISK=@FileName WITH FORMAT END ELSE BEGIN --差别备份 SET @FileName = 'E:\DBBackup\DBName.9197\DBName.9197_Primary_Diff_' + @CurrentTime+'.bak' BACKUP DATABASE [DBName.9197] FILEGROUP='PRIMARY' TO DISK=@FileName WITH DIFFERENTIAL,FORMAT END GO --3设置简单模式 USE [master] GO ALTER DATABASE [DBName.9197] SET RECOVERY SIMPLE WITH NO_WAIT GO
(二) 下面的代码实现了删除备份文件,从下面的代码中能够看出删除了14天以前的备份文件;
--删除以前的备份文件 DECLARE @DeleteDate DATETIME SET @DeleteDate = DATEADD(DAY, -14, GETDATE()) EXECUTE MASTER.SYS.XP_DELETE_FILE 0, N'E:\DBBackup\DBName.9197\', N'bak', @DeleteDate
(三) 下面的代码实现了批量建立文件夹,须要开启xp_cmdshell开关,使用了游标循环数据库名进行建立文件夹;
--批量建立文件夹 EXEC sp_configure 'show advanced options', 1 RECONFIGURE EXEC sp_configure 'xp_cmdshell', 1 RECONFIGURE DECLARE @DBName VARCHAR(100) DECLARE @SQL VARCHAR(1000) DECLARE CurDBName CURSOR FOR SELECT name FROM sys.databases WHERE name LIKE '%Opinion%' AND STATE =0 OPEN CurDBName FETCH NEXT FROM CurDBName INTO @DBName WHILE @@FETCH_STATUS = 0 BEGIN SET @SQL = 'mkdir E:\DBBackup\' + @DBName EXEC xp_cmdshell @SQL FETCH NEXT FROM CurDBName INTO @DBName END CLOSE CurDBName DEALLOCATE CurDBName EXEC sp_configure 'show advanced options', 0 RECONFIGURE EXEC sp_configure 'xp_cmdshell', 0 RECONFIGURE
(四) 下面的代码实现了批量建立做业,这里有一个建立做业的模板:JobTemplet.sql,我写了一个Replaced.bat的批处理文件,这个批处理文件替换模板文件中数据库的字符串,并生成建立新数据库做业的SQL脚本,建立以后就会执行这个SQL脚原本建立做业;
1. 使用上面提供的SQL代码,建立好做业的步骤和计划,再使用SSMS生成建立做业的脚本,这个就是JobTemplet.sql;
USE [msdb] GO /****** 对象: Job [Barefoot_Opinion_9565] 脚本日期: 01/06/2013 14:07:27 ******/ BEGIN TRANSACTION DECLARE @ReturnCode INT SELECT @ReturnCode = 0 /****** 对象: JobCategory [Database Maintenance] 脚本日期: 01/06/2013 14:07:27 ******/ IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'Database Maintenance' AND category_class=1) BEGIN EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Database Maintenance' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback END DECLARE @jobId BINARY(16) EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'DBName_9565', @enabled=1, @notify_level_eventlog=0, @notify_level_email=0, @notify_level_netsend=0, @notify_level_page=0, @delete_level=0, @description=N'备份主分区', @category_name=N'Database Maintenance', @owner_login_name=N'oofraBnimdA_gz', @job_id = @jobId OUTPUT IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback /****** 对象: Step [Bakcup] 脚本日期: 01/06/2013 14:07:27 ******/ EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Bakcup', @step_id=1, @cmdexec_success_code=0, @on_success_action=3, @on_success_step_id=0, @on_fail_action=2, @on_fail_step_id=0, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'TSQL', @command=N'--1设置完整模式 USE [master] GO ALTER DATABASE [DBName.9565] SET RECOVERY FULL WITH NO_WAIT GO --2备份主分区(完整备份) DECLARE @CurrentTime VARCHAR(50), @FileName VARCHAR(200) SET @CurrentTime = REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120 ),''-'',''_''),'' '',''_''),'':'','''') IF(DATEPART(DW, GETDATE()) = 2)--星期一 BEGIN SET @FileName = ''E:\DBBackup\DBName.9565\DBName.9565_Primary_Full_'' + @CurrentTime+''.bak'' BACKUP DATABASE [DBName.9565] FILEGROUP=''PRIMARY'' TO DISK=@FileName WITH FORMAT END ELSE BEGIN SET @FileName = ''E:\DBBackup\DBName.9565\DBName.9565_Primary_Diff_'' + @CurrentTime+''.bak'' BACKUP DATABASE [DBName.9565] FILEGROUP=''PRIMARY'' TO DISK=@FileName WITH DIFFERENTIAL,FORMAT END GO --3设置简单模式 USE [master] GO ALTER DATABASE [DBName.9565] SET RECOVERY SIMPLE WITH NO_WAIT GO ', @database_name=N'DBName.9565', @flags=0 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback /****** 对象: Step [Delete] 脚本日期: 01/06/2013 14:07:27 ******/ EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Delete', @step_id=2, @cmdexec_success_code=0, @on_success_action=1, @on_success_step_id=0, @on_fail_action=2, @on_fail_step_id=0, @retry_attempts=0, @retry_interval=0, @os_run_priority=0, @subsystem=N'TSQL', @command=N'--删除以前的备份文件 DECLARE @DeleteDate DATETIME SET @DeleteDate = DATEADD(DAY, -14, GETDATE()) EXECUTE MASTER.SYS.XP_DELETE_FILE 0, N''E:\DBBackup\DBName.9565\'', N''bak'', @DeleteDate', @database_name=N'DBName.9565', @flags=0 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId, @name=N'Plan', @enabled=1, @freq_type=8, @freq_interval=34, @freq_subday_type=1, @freq_subday_interval=0, @freq_relative_interval=0, @freq_recurrence_factor=1, @active_start_date=20130105, @active_end_date=99991231, @active_start_time=10000, @active_end_time=235959 IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)' IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback COMMIT TRANSACTION GOTO EndSave QuitWithRollback: IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION EndSave:
2. 把下面的代码保存为Replaced.bat文件,这个批处理文件接受两个参数,一个是@OldStr一个是@NewStr,即旧的数据库名和新的数据库名;
@if "%1"=="" goto error_parm @if "%2"=="" goto error_parm @echo off setlocal enabledelayedexpansion set file=E:\DBBackup\JobTemplet.sql set "file=%file:"=%" for %%i in ("%file%") do set file=%%~fi echo. set replaced=%1 echo. set all=%2 for /f "delims=" %%i in ('type "%file%"') do ( set str=%%i set "str=!str:%replaced%=%all%!" echo !str!>>"%file%"_%2.sql )
3. 到这里咱们已经有JobTemplet.sql和Replaced.bat文件了,咱们再建立一个Replaced_Test.bat批处理进行测试,这个批处理设置两个变量,一个是@OldStr一个是@NewStr,再调用Replaced.bat来替换字符串;
@set temp1=9565
@set temp2=9001
call Replaced_2.bat %temp1%,%temp2%
经过上面3个步骤,建立了3个文件,以下图所示:
(Figure4:建立的模板和批处理文件)
4. 下面的SQL代码就是使用游标的形式,循环符合条件的数据库,使用批处理替换模板文件,生成新的T-SQL文件,再执行新生成的T-SQL文件,这样就建立了游标当前数据库的备份做业了:
-- ============================================= -- Author: <听风吹雨> -- Blog: <http://gaizai.cnblogs.com/> -- Create date: <2013/12/03> -- Description: <批量建立做业的T-SQL文件(替换字符串),并执行这个T-SQL文件> -- ============================================= --开启高级功能 EXEC sp_configure 'show advanced options', 1 RECONFIGURE EXEC sp_configure 'xp_cmdshell', 1 RECONFIGURE DECLARE @DBName VARCHAR(100)--数据库名称 DECLARE @CmdFile VARCHAR(1000)--建立File的Cmd命令字符串 DECLARE @CmdJob VARCHAR(1000)--执行Job的Cmd命令字符串 DECLARE @OldStr varchar(100)--须要替换的字符串 DECLARE @NewStr varchar(100)--替换成字符串 SET @OldStr = '9565' --查找符合条件的数据库 DECLARE CurDBName CURSOR FOR SELECT name FROM sys.databases WHERE name LIKE '%Opinion%' AND STATE =0 OPEN CurDBName FETCH NEXT FROM CurDBName INTO @DBName WHILE @@FETCH_STATUS = 0 BEGIN --使用批处理替换模板文件,生成新的T-SQL文件 SET @NewStr = SUBSTRING(@DBName,LEN('DBName.')+1,LEN(@DBName)) SET @CmdFile = 'E:\DBBackup\Replaced.bat ' + @OldStr + ' ' + @NewStr PRINT @CmdFile EXEC xp_cmdshell @CmdFile --执行新生成的T-SQL文件 SET @CmdJob = 'sqlcmd -i"E:\DBBackup\JobTemplet.sql _'+@NewStr+'.sql"' PRINT @CmdJob EXEC xp_cmdshell @CmdJob --循环下一个数据库 FETCH NEXT FROM CurDBName INTO @DBName END CLOSE CurDBName DEALLOCATE CurDBName --关闭高级功能 EXEC sp_configure 'xp_cmdshell', 0 RECONFIGURE EXEC sp_configure 'show advanced options', 0 RECONFIGURE
上面的脚本中你须要注意两点,以下图所示:“bat”字符串后面是有一个空格的,若是没有这个空格就会报错的;另一点就是“sql”字符串与“_”之间也是有个空格的,这个空格是由于在使用Replaced.bat批处理建立文件的文件名的时候多一个空格(暂时还没找出缘由),因此这里的文件名称须要加一个空格;
执行完上面的脚本以后,在E:\DBBackup的目录下会生成下图所示的SQL脚本文件:
(五) 到这里,SSMS中已经建立了10个做业,就以下图所显示:
(Figure7:建立的做业列表)
可是为了能分散做业的执行时间,咱们有两种方式作到这点:
1) 在JobTemplet.sql中有一个参数@active_start_time=10000,这个参数的意思是在深夜1点钟的时候执行做业,因此能够在Replaced.bat这个批处理再加一个参数,而且在JobTemplet.sql中替换成咱们想要的值,在外层T-SQL调用Replaced.bat的时候记录这个值,并传入到Replaced.bat中(这就留给读者本身实现吧,我只实现了下面的第二种方法);
2) 修改已经建立做业的执行时间,下面的代码实现了批量修改做业的执行时间,使用游标的形式,循环调用msdb.dbo.sp_update_schedule修改做业的执行时间;
USE [msdb] GO -- ============================================= -- Author: <听风吹雨> -- Blog: <http://gaizai.cnblogs.com/> -- Create date: <2013/12/03> -- Description: <批量修改Job备份时间> -- ============================================= DECLARE @DBName VARCHAR(100) DECLARE @ScheduleId INT DECLARE @Date DATETIME DECLARE @Time VARCHAR(50) DECLARE @SQL VARCHAR(1000) SET @Date = '2013-01-08 00:20:00.000' DECLARE CurDBName CURSOR FOR SELECT name,schedule_id FROM SYSJOBS AS J LEFT JOIN [SYSJOBSCHEDULES] AS S ON J.job_id= S.job_id WHERE NAME LIKE 'DBName_%' ORDER BY NAME OPEN CurDBName FETCH NEXT FROM CurDBName INTO @DBName,@ScheduleId WHILE @@FETCH_STATUS = 0 BEGIN --修改做业的执行时间 SET @Time = REPLACE(CONVERT(VARCHAR, @Date, 8 ),':','') SET @SQL = 'EXEC msdb.dbo.sp_update_schedule @schedule_id = ''' + CONVERT(VARCHAR(50),@ScheduleId) + ''', @active_start_time=' + @Time PRINT(@DBName +':'+ @SQL) EXEC(@SQL) --递增分钟 SET @Date = DATEADD(mi,20,@Date) --Get Next DataBase FETCH NEXT FROM CurDBName INTO @DBName,@ScheduleId END CLOSE CurDBName DEALLOCATE CurDBName
(六) 下面的代码实现了批量删除做业,使用游标的形式,循环调用sp_delete_job;
USE [msdb] GO -- ============================================= -- Author: <听风吹雨> -- Blog: <http://gaizai.cnblogs.com/> -- Create date: <2013/12/03> -- Description: <批量删除Job> -- ============================================= DECLARE @DBName VARCHAR(100) DECLARE CurDBName CURSOR FOR SELECT name FROM msdb.dbo.sysjobs OPEN CurDBName FETCH NEXT FROM CurDBName INTO @DBName WHILE @@FETCH_STATUS = 0 BEGIN --删除Job exec sp_delete_job @job_name = @DBName --Get Next DataBase FETCH NEXT FROM CurDBName INTO @DBName END CLOSE CurDBName DEALLOCATE CurDBName
(七) 查看做业的运行状况;
--查询做业的执行状况 SELECT b.name,b.enabled,b.description,b.date_created,b.date_modified, a.step_id,a.step_name,message,run_date,run_time,run_duration FROM [msdb].dbo.[sysjobhistory] AS a LEFT JOIN [msdb].[dbo].[sysjobs] AS b ON a.job_id = b.job_id ORDER BY name
(Figure8:做业执行状况)
--查询做业与计划的对应关系 SELECT J.name,schedule_id FROM [msdb].[dbo].[sysjobs] AS J LEFT JOIN [msdb].[dbo].[sysjobschedules] AS S on J.job_id= S.job_id WHERE J.name LIKE '%' ORDER BY J.name
(Figure9:做业与计划的对应关系)
在表[msdb].[dbo].[sysschedules]中也一样包含做业计划信息;
(Figure10:sysschedules信息)
既然作了上面主文件组的备份,固然咱们须要去测试这个主文件组的还原了,这样才能够当遇到问题能够快速还原备份文件,达到还原数据的目的;
接下来会在另一篇文章里面专门讲解;
sp_update_schedule (Transact-SQL)