Déplacement des fichiers d’une base de données.

Cette procédure permet de déplacer les fichiers d’une base de données. LA procédure esr données « as is ».


CREATE PROCEDURE [dbo].[P_MOVE_FILE_DATABASE]  
                 @DB_NAME         sysname,
                 @DB_DATAFILE_DIR NVARCHAR(1024),
                 @DB_TRANFILE_DIR NVARCHAR(1024) = NULL
AS
/******************************************************************************
* Frédéric Brouard / alias SQLpro - Expert Microsoft SQL Server / SGBDR / SQL *
* Sté. SQL spot http://www.sqlspot.com  -  audit, tuning, conseil, formations *
* Enseignant Arts et Métiers, ISEN, EXIA (CESI) - Conférencier U. Toulouse II *
*******************************************************************************
* @DB_NAME : nom de la base                                                   *
* @DB_DATAFILE_DIR : nouveau répertoire des fichiers de données               *
* @DB_TRANFILE_DIR : nouveau répertoire des fichiers de transactions          *
*******************************************************************************
* © Frédéric Brouard / SQLspot -  2010-04-27  -  http://sqlpro.developpez.com *
******************************************************************************/
SET NOCOUNT ON;
-- vérification de saisie du répertoire de destination
IF @DB_DATAFILE_DIR IS NULL
BEGIN
   RAISERROR('Le répertoire de destination de la base n''a pas été indiqué.', 16, 1, @DB_NAME);
   RETURN;
END;
-- teste si le répertoire est bien définit :
IF SUBSTRING(REVERSE(@DB_DATAFILE_DIR), 1, 1) <> '\'
   SET @DB_DATAFILE_DIR = @DB_DATAFILE_DIR +'\';
-- si le repertoire de destination du JT n'est pas précisé, prendre celui des données
IF @DB_TRANFILE_DIR IS NULL  
   SET @DB_TRANFILE_DIR = @DB_DATAFILE_DIR
ELSE
   IF SUBSTRING(REVERSE(@DB_TRANFILE_DIR), 1, 1) <> '\'
      SET @DB_TRANFILE_DIR = @DB_TRANFILE_DIR +'\';
-- on vérifie si cette base existe bien
IF NOT EXISTS(SELECT *
              FROM   sys.databases
              WHERE  name = @DB_NAME)
BEGIN
   RAISERROR('la base de données %s n''existe pas.', 16, 1, @DB_NAME);
   RETURN;
END;
-- configuration du serveur pour l'activation des commandes OS
DECLARE @SQL NVARCHAR(max);
DECLARE @SHOW BIT, @SHEL BIT;
DECLARE @CONFIG TABLE (CFG_NAME  sysname,
                       CFG_MIN   INT,
                       CFG_MAX   INT,
                       CFG_VALUE INT,
                       CFG_RUN   INT);
INSERT INTO @CONFIG
EXEC sp_configure 'show advanced options';
SELECT @SHOW = CFG_RUN FROM @CONFIG;
IF @SHOW =0
BEGIN  
   SET @SQL = 'EXEC sp_configure ''show advanced options'', 1;'
   EXEC (@SQL);
   SET @SQL = 'RECONFIGURE'
   EXEC (@SQL);
END;
DELETE FROM @CONFIG;
INSERT INTO @CONFIG
EXEC sp_configure 'xp_cmdshell';
SELECT @SHEL = CFG_RUN FROM @CONFIG;
IF @SHEL =0
BEGIN  
   SET @SQL = 'EXEC sp_configure ''xp_cmdshell'', 1;'
   EXEC (@SQL);
   SET @SQL = 'RECONFIGURE'
   EXEC (@SQL);
END;
-- vérification de l'existence du répertoire
EXEC master.dbo.xp_subdirs @DB_DATAFILE_DIR
IF @@ERROR <> 0
BEGIN
   RAISERROR('Le répertoire de stockage des données %s n''existe pas.', 16, 1, @DB_DATAFILE_DIR);
   GOTO LBL_RESUME;
END;
EXEC master.dbo.xp_subdirs @DB_TRANFILE_DIR
IF @@ERROR <> 0
BEGIN
   RAISERROR('Le répertoire de stockage du journal de transaction %s n''existe pas.', 16, 1, @DB_TRANFILE_DIR);
   GOTO LBL_RESUME;
END;  
-- on récupére l'emplacement, le nom et la taille des fichiers
DECLARE @FILES TABLE (FLS_ID            INT,
                      FLS_PATH          NVARCHAR(1024),
                      FLS_TYPE          SMALLINT,
                      FLS_NAME          NVARCHAR(256),
                      FLS_LOGIQUE       sysname,
                      FLS_SIZE          BIGINT,          
                      FLS_OK            BIT,
                      FLS_NEWPATH       NVARCHAR(1024));
-- mise à jour des fichiers pour taille exacte                      
SET @SQL = 'DBCC UPDATEUSAGE (''' + @DB_NAME + ''')';
EXEC (@SQL);
INSERT INTO @FILES                      
SELECT "file_id",  
       REVERSE(SUBSTRING(REVERSE(physical_name), CHARINDEX('\', REVERSE(physical_name)), LEN(physical_name)+1)),  
       "type",
       REVERSE(SUBSTRING(REVERSE(physical_name), 1, CHARINDEX('\', REVERSE(physical_name))-1)),
       name, (size / 128) + 1, 1,
       CASE
          WHEN "type" = 0 THEN @DB_DATAFILE_DIR
          WHEN "type" = 1 THEN @DB_TRANFILE_DIR
          ELSE NULL
       END
FROM   sys.master_files
WHERE  database_id = DB_ID(@DB_NAME);  
-- test si "fichiers" non déplaçable
IF EXISTS(SELECT *
          FROM   @FILES
          WHERE  FLS_TYPE > 1)
BEGIN
   RAISERROR('la base de données %s contient au moins un fichier non déplaçable (FILESTREAM par exemple).', 16, 1, @DB_NAME);
   RETURN;
END;
-- vérification : si un seul fichier existe déjà dans les nouveaux répertoires alors on abandonne.
DECLARE @CMD NVARCHAR(1024);
DECLARE @F TABLE (F_EXISTS BIT,
                  F_DIR    BIT,
                  F_ADIR   BIT);
DECLARE C CURSOR
FOR  
   SELECT FLS_NEWPATH + FLS_NAME AS F
   FROM   @FILES
FOR UPDATE OF FLS_OK;  
OPEN C;
FETCH C INTO @CMD;
WHILE @@FETCH_STATUS = 0
BEGIN
   DELETE FROM @F;
   INSERT INTO @F
   EXEC master.dbo.xp_fileexist @CMD;
   IF EXISTS(SELECT * FROM @F WHERE F_EXISTS = 1)
      UPDATE @FILES
      SET    FLS_OK = 0
      WHERE  CURRENT OF C;
   FETCH C INTO @CMD;
END;
CLOSE C;
DEALLOCATE C;
-- y a t-il des fichiers non OK ?
IF EXISTS(SELECT *
          FROM   @FILES
          WHERE  FLS_OK = 0)                      
BEGIN
   SET @SQL = 'Déplacement impossible : des fichiers ne pourrons pas être créés car ils existent déjà. (';
   SELECT @SQL = @SQL  
               + CASE  
                    WHEN FLS_TYPE = 0 THEN @DB_DATAFILE_DIR
                    WHEN FLS_TYPE = 1 THEN @DB_TRANFILE_DIR
                 END + FLS_NAME + ', '
   FROM   @FILES
   WHERE  FLS_OK = 0;
   SET @SQL = SUBSTRING(@SQL, 1, LEN(@SQL) - 1) + ')';
   RAISERROR(@SQL, 16, 1);
   RETURN;
END;
-- si la capacité ne permet pas de recréer le fichier, alors on abandonne
DECLARE @U TABLE (U_DRIVE_LETTER NVARCHAR(128),
                  U_FREESIZE_MO  BIGINT);
INSERT INTO @U                  
EXEC master.dbo.xp_fixeddrives;
 
IF EXISTS(SELECT U.U_DRIVE_LETTER
          FROM   (SELECT SUBSTRING(FLS_NEWPATH, 1, 1) AS FLS_U, SUM(FLS_SIZE) AS FLS_SZ
                  FROM   @FILES
                  GROUP  BY SUBSTRING(FLS_NEWPATH, 1, 1)) AS F
                 INNER JOIN @U AS U
                       ON F.FLS_U = U.U_DRIVE_LETTER
                          AND F.FLS_SZ > U.U_FREESIZE_MO)                          
BEGIN
   SET @SQL = 'Déplacement impossible : la capacité d''une des unités de stockage ne permet pas le déplacement. (';
   SELECT @SQL = @SQL + U_DRIVE_LETTER + ', '
   FROM   (SELECT SUBSTRING(FLS_NEWPATH, 1, 1) AS FLS_U, SUM(FLS_SIZE) AS FLS_SZ
           FROM   @FILES
           GROUP  BY SUBSTRING(FLS_NEWPATH, 1, 1)) AS F
          INNER JOIN @U AS U
                ON F.FLS_U = U.U_DRIVE_LETTER
                   AND F.FLS_SZ > U.U_FREESIZE_MO;
   SET @SQL = SUBSTRING(@SQL, 1, LEN(@SQL) - 1) + ')';
   RAISERROR(@SQL, 16, 1);
   RETURN;
END;
-- on vire les utilisateurs de la base
SET @SQL = 'ALTER DATABASE [' + @DB_NAME +'] SET SINGLE_USER WITH ROLLBACK IMMEDIATE'
EXEC (@SQL);
-- on détache la base
EXEC sp_detach_db @DB_NAME;
-- on transfère les fichiers
DECLARE C CURSOR
FOR  
   SELECT 'MOVE /Y "' + FLS_PATH + FLS_NAME + '" "'  
              + FLS_NEWPATH + FLS_NAME + '"'
   FROM   @FILES
FOR READ ONLY;  
OPEN C;
FETCH C INTO @CMD;
WHILE @@FETCH_STATUS = 0
BEGIN
   EXEC master.dbo.xp_cmdshell @CMD;
   FETCH C INTO @CMD;
END;
CLOSE C;
DEALLOCATE C;
-- on recrée la base à partir des fichiers déplacés.
SET @SQL = 'CREATE DATABASE [' + @DB_NAME +'] ON ('
SELECT @SQL = @SQL + ' NAME = [' + FLS_LOGIQUE +'], '
                   + ' FILENAME = '''  
                   + FLS_NEWPATH + FLS_NAME + '''), ('
FROM   @FILES;
SET @SQL = SUBSTRING(@SQL, 1, LEN(@SQL) -3) + ' FOR ATTACH;';
EXEC (@SQL);
-- finalisation : remplacement des options du serveur à l'original
LBL_RESUME:
IF @SHEL = 0
BEGIN
   SET @SQL = 'EXEC sp_configure ''xp_cmdshell'', 0;'
   EXEC (@SQL);
   SET @SQL = 'RECONFIGURE'
   EXEC (@SQL);
END;
IF @SHOW = 0
BEGIN
   SET @SQL = 'EXEC sp_configure ''show advanced options'', 0;'
   EXEC (@SQL);
   SET @SQL = 'RECONFIGURE'
   EXEC (@SQL);
END;

--------
Frédéric Brouard, SQLpro - ARCHITECTE DE DONNÉES, http://sqlpro.developpez.com/
Expert bases de données relationnelles et langage SQL. MVP Microsoft SQL Server
www.sqlspot.com : modélisation, conseil, audit, optimisation, tuning, formation
* * * * *  Enseignant CNAM PACA - ISEN Toulon - CESI Aix en Provence  * * * * *

MVP Microsoft SQL Server

Laisser un commentaire