-- à partir de la version 2012
DROP PROCEDURE dbo.P_BACKUP_ALL_DATABASE
GO
CREATE PROCEDURE dbo.P_BACKUP_ALL_DATABASE
@BACKUP_TYPE CHAR(1), -- type of backup F for FULL, D for Differential, T for Logs (transactional)
@FILE_PATH NVARCHAR(256), -- path to store the backups. If NULL store the backups in the défault backup path
@FILE_EXT NVARCHAR(8) , -- if NULL, BAK for Full, DIF for Differential and TRN for Logs (transactions)
@FILE_NAME NVARCHAR(128), -- if NULL create one file per database to be backuped, if valued stacks all the backups in only one nammed file
@FILE_DATE BIT, -- if 1 add date in file name
@FILE_TIME BIT, -- if 1 add time in file name
@FILE_FORMAT BIT, -- if 1 format the existing file
@UTC_DATETIME BIT, -- if 0 datetime is in local time (by default, UTC datetime)
@ONLY_USER_DB BIT, -- if 1 backup only user's databases
@EXCLUDE_DB XML, -- list of databases to exclude in the form of 'database_name1database_name2'
@COMPRESSION BIT -- if one add compression
AS
/******************************************************************************
* MODULE : BACKUP *
* NATURE : PROCEDURE *
* OBJECT : dbo.P_BACKUP_ALL_DATABASE *
* CREATE : 2018-05-29 *
* VERSION : 1 *
*******************************************************************************
* Frédéric BROUARD - alias SQLpro - SARL SQL SPOT - SQLpro@sqlspot.com *
* Architecte de données : expertise, audit, conseil, formation, modélisation *
* tuning, sur les SGBD Relationnels, le langage SQL, MS SQL Server/PostGreSQL *
* blog: http://mssqlserver.fr website: http://sqlpro.developpez.com *
* Most Valuable Professional - MVP - SQL Server (data platform) depuis 2004 *
*******************************************************************************
* PURPOSE : backup all databases in an instance *
*******************************************************************************
* INPUTS : *
* @BACKUP_TYPE CHAR(1) -- type of backup F for FULL, D for *
* Differential, T for Logs (transactional) *
* @FILE_PATH NVARCHAR(256) -- path to store the backups. If NULL store *
* the backups in the défault backup path *
* @FILE_EXT NVARCHAR(8) -- if NULL BAK for Full DIF for Differential *
* and TRN for Logs (transactions) *
* @FILE_NAME NVARCHAR(128) -- if NULL create one file per database to *
* be backuped, if valued stacks all the *
* backups in the only file you nammed *
* @FILE_DATE BIT -- if 1 add date in file name *
* @FILE_TIME BIT -- if 1 add time in file name *
* @FILE_FORMAT BIT -- if 1 format the existing file *
* @UTC_DATETIME BIT -- if 0 datetime is in local time, if 1 UTC *
* @ONLY_USER_DB BIT -- if 1 backup only user's databases *
* @EXCLUDE_DB XML -- list of databases to exclude. Patterne is *
* ' *
* database_name1 *
* database_name2 *
* ... *
* ' *
* @COMPRESSION BIT -- if one add compression *
*******************************************************************************
* OUPUT : none *
*******************************************************************************
* RUNNABLE : version 2012 and superior *
* must have sysdamin role or equivalent (for of logging errors) *
*******************************************************************************
* SAMPLE : *
* EXEC dbo.P_BACKUP_ALL_DATABASE *
* @BACKUP_TYPE = 'F', *
* @FILE_PATH = 'C:\DATA', *
* @FILE_EXT = NULL, *
* @FILE_NAME = NULL, *
* @FILE_DATE = 1, *
* @FILE_TIME = 1, *
* @FILE_FORMAT = 1, *
* @UTC_DATETIME = 1, *
* @ONLY_USER_DB = 1, *
* @EXCLUDE_DB = N' *
* ReportServer$SQL2016FBIN2 *
* ReportServer$SQL2016FBIN2TempDB *
* ', *
* @COMPRESSION = 1 *
******************************************************************************/
SET NOCOUNT ON;
-- verify if @BACKUP_TYPE is F, D or T
IF @BACKUP_TYPE COLLATE Latin1_General_CI_AS NOT IN ('F', 'D', 'T')
BEGIN
RAISERROR('The backup type "%s" is not a valid one. Must be "F" for FULL, "D" for Differentiel or "T" for Transactional (logs).', 16, 1, @BACKUP_TYPE);
RETURN;
END;
--verify if @FILE_EXT is composed only of letters, figures and underscore
IF NOT @FILE_EXT COLLATE Latin1_General_CI_AS LIKE REPLICATE('[A-Z0-9_]', LEN(@FILE_EXT)) OR @FILE_EXT = ''
BEGIN
RAISERROR('The file extension %s is not a valid one. Must be composed of letters, figure and underscore.', 16, 1, @FILE_EXT);
RETURN;
END;
--verify if @FILE_EXT is composed only of letters, figures and underscore
IF NOT @FILE_NAME COLLATE Latin1_General_CI_AS LIKE REPLICATE('[A-Z0-9_]', LEN(@FILE_NAME)) OR @FILE_NAME = ''
BEGIN
RAISERROR('The file name %s is not a valid one. Must be composed of letters, figure and underscore.', 16, 1, @FILE_NAME);
RETURN;
END;
-- verify if directory exists
IF @FILE_PATH IS NOT NULL
BEGIN
DECLARE @TF TABLE (file_exists BIT, file_is_dir BIT, parent_file BIT);
INSERT INTO @TF
EXEC xp_fileexist @FILE_PATH;
IF NOT EXISTS(SELECT * FROM @TF WHERE file_is_dir = 1)
BEGIN
RAISERROR('The directory %s must exists', 16, 1, @FILE_PATH);
RETURN;
END;
END
-- add backslash at the end if not exists
IF RIGHT(@FILE_PATH, 1) <> CHAR(92)
SET @FILE_PATH = @FILE_PATH + CHAR(92);
SELECT ASCII('\')
-- verify the XML syntax of the database list to exclude
IF @EXCLUDE_DB IS NOT NULL
BEGIN
BEGIN TRANSACTION;
BEGIN TRY
DECLARE @XQL NVARCHAR(max);
SET @XQL = 'USE msdb;CREATE XML SCHEMA COLLECTION dbo.XSC_3A586931_C43F_47C1_A419_6E19DB23CEC3
AS N''
'';'
EXEC (@XQL);
SET @XQL = N'USE msdb;CREATE TABLE dbo.XTB_3A586931_C43F_47C1_A419_6E19DB23CEC3 (data_bases XML (XSC_3A586931_C43F_47C1_A419_6E19DB23CEC3));'
EXEC (@XQL)
SET @XQL = N'USE msdb;INSERT INTO dbo.XTB_3A586931_C43F_47C1_A419_6E19DB23CEC3 VALUES (''' + CAST(@EXCLUDE_DB AS varchar(max)) + N''');'
EXEC (@XQL);
ROLLBACK;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK;
THROW 55555, 'XML list of databases to exclude is in an uncorrect format. Must be formed like "database_name1database_name2".', 1;
RETURN;
END CATCH;
END;
-- local variables
DECLARE @dbid INT, -- the database id
@dbname sysname, -- the name of the database
@f_name sysname, -- file name for backup
@NEWNAME sysname, -- corrected file name
@I TINYINT, -- loop indice
@SQL NVARCHAR(max), -- SQL code to be dynamically executed
@DT DATETIME, -- system datetime
@D NCHAR(9), -- backup date encoded as aaaammjjD (like 20181231D)
@T NCHAR(9), -- backup time encoded as hhHmmMssS (like 21H38M05S)
@ERRORS SMALLINT, -- number of errors
@ERRMSG VARCHAR(1024); -- error message
-- find the candidate databases
CREATE TABLE #dbs (database_id INT PRIMARY KEY, dbname sysname, f_name NVARCHAR(128));
INSERT INTO #dbs (database_id, dbname)
SELECT database_id, name
FROM sys.databases
WHERE state = 0
AND source_database_id IS NULL -- exclude snapshot database
AND name NOT IN ('tempdb') -- exclude tempdb
AND CASE WHEN @BACKUP_TYPE = 'T' -- transactional backup only if recovery model <> SIMPLE
AND recovery_model = 3
THEN 0
ELSE 1
END = 1
AND CASE WHEN @ONLY_USER_DB = 1 -- excluding system database if required
AND name IN ('master', 'msdb', 'model')
THEN 0
ELSE 1
END = 1 -- excluding database from the XML list
AND name NOT IN (SELECT db_name.value('(.)[1]', 'sysname')
FROM @EXCLUDE_DB.nodes('/dbs/db') AS TX(db_name));
-- correcting file name to have only letters without accents
UPDATE #dbs
SET f_name = CAST(dbname collate SQL_Latin1_General_Cp1251_CS_AS AS varchar(128));
-- correcting file name with only letters (A to Z), figures (0 to 9) and underscore (_)
DECLARE C1 CURSOR
LOCAL FORWARD_ONLY
FOR SELECT database_id, f_name
FROM #dbs
FOR UPDATE OF f_name;
OPEN C1;
FETCH C1 INTO @dbid, @f_name;
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @I = 1, @NEWNAME = N'';
WHILE @I <= LEN(@f_name)
BEGIN
IF SUBSTRING(@f_name, @I, 1) LIKE '[A-Z0-9_]' COLLATE Latin1_General_CI_AS
SET @NEWNAME = @NEWNAME + SUBSTRING(@f_name, @I, 1);
ELSE
SET @NEWNAME = @NEWNAME + N'_';
SET @I = @I + 1;
END
UPDATE #dbs
SET f_name = @NEWNAME
WHERE CURRENT OF C1;
FETCH C1 INTO @dbid, @f_name;
END
CLOSE C1;
DEALLOCATE C1;
SET @ERRORS = 0;
-- we use cursor to backup databases one by one, which facilitate the rollback
DECLARE C2 CURSOR
LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR SELECT dbname, f_name
FROM #dbs;
OPEN C2;
FETCH C2 INTO @dbname, @f_name;
-- loop over the databases
WHILE @@FETCH_STATUS = 0
BEGIN
-- calculate date and time as strings if needed
IF @FILE_DATE = 1 OR @FILE_TIME = 1
SET @DT = CASE @UTC_DATETIME WHEN 0 THEN GETDATE() ELSE GETUTCDATE() END;
IF @FILE_DATE = 1 AND @FILE_NAME IS NOT NULL AND @D IS NULL
SET @D = CONVERT(CHAR(8), @DT, 112) + 'D';
IF @FILE_TIME = 1 AND @FILE_NAME IS NOT NULL AND @T IS NULL
SET @T = FORMAT(CONVERT(TIME, @DT, 108), 'hh\Hmm\Mss\S');
-- compose the backup sql command
SET @SQL = N'BACKUP '
+ CASE WHEN @BACKUP_TYPE = N'T' THEN N'LOG' ELSE N'DATABASE' END
+ N' [' + @dbname + N'] TO DISK = '''
+ COALESCE(@FILE_PATH, N'')
+ CASE
WHEN @FILE_NAME IS NOT NULL
THEN @FILE_NAME
ELSE @f_name
END
+ CASE WHEN @FILE_DATE = 1 THEN N'_' + @D ELSE '' END
+ CASE WHEN @FILE_TIME = 1 THEN N'_' + @T ELSE '' END
+ CASE WHEN @UTC_DATETIME = 1 THEN N'_UTC' ELSE '' END
+ N'.'
+ CASE
WHEN @FILE_EXT IS NOT NULL
THEN @FILE_EXT
ELSE
CASE @BACKUP_TYPE
WHEN 'F' THEN 'BAK'
WHEN 'D' THEN 'DIF'
WHEN 'T' THEN 'TRN'
ELSE ''
END
END
+ N''' WITH '
+ CASE WHEN @FILE_FORMAT = 1 THEN N'FORMAT' ELSE N'NOFORMAT' END
+ CASE WHEN @BACKUP_TYPE = N'D' THEN N', DIFFERENTIAL' ELSE '' END
+ CASE WHEN @COMPRESSION = 1 THEN N', COMPRESSION' ELSE '' END
+ N';'
-- execute the SQL backup command
BEGIN TRY
EXEC (@SQL);
END TRY
BEGIN CATCH
-- write a log message if in error
SET @ERRMSG = ERROR_MESSAGE();
RAISERROR ('>> BACKUP: %s (SQL command: %s).', 16, 1, @ERRMSG, @SQL) WITH LOG;
SET @ERRORS = @ERRORS + 1;
FETCH C2 INTO @dbname, @f_name;
CONTINUE;
END CATCH
FETCH C2 INTO @dbname, @f_name;
END;
CLOSE C2;
DEALLOCATE C2;
IF @ERRORS >0
THROW 55555, 'Almost one backup exception has been raised. Check SQL logs to find entries beginning with ">> BACKUP:".', 1;
GO
EXEC dbo.P_BACKUP_ALL_DATABASE
@BACKUP_TYPE = 'T',
@FILE_PATH = NULL,
@FILE_EXT = NULL,
@FILE_NAME = 'mes_bases',
@FILE_DATE = 1,
@FILE_TIME = 1,
@FILE_FORMAT = 1,
@UTC_DATETIME = 1,
@ONLY_USER_DB = 1,
@EXCLUDE_DB = N'ReportServer$SQL2016FBIN2ReportServer$SQL2016FBIN2TempDB',
@COMPRESSION = 1