Procédure de déplacement de fichier de bases de données MS SQL Server

Voici une procédure générique de déplacement de fichiers pour une base de données Microsoft SQL Server.

FONCTIONNEMENT :
Après avoir vérifié les paramètres, la procédure exécute séquentiellement :
1 – un détachement de la base (par la procédure sp_detach_db) après être devenu l’utilisateur unique ( avec le commande ALTER DATABASE … SET SINGLE_USER WITH ROLLBACK IMMEDIATE)
2 Рun transfert de fichier (par la proc̩dure xp_cmdshell)
3 – un rattachement des fichiers (par la commande CREATE DATABASE … FOR ATTACH)

PARAMÈTRES :
@DB_NAME : nom de la base (doit exister, contrôle préventif effectué)
@DESTINATION : répertoire de destination (doit exister, contrôle préventif effectué)
@LOGICAL_FILE : fichier(s) à déplacer – Ce dernier paramètre peut porter sur un nom logique de fichier dans la base (voir name dans sys.database_files) ou alors, pour un déplacement générique :
[ALL] (crochets compris) pour tous les fichiers;
[DATA] (crochets compris) pour les fichiers de données;
[TRAN] (crochets compris) pour les fichiers du journal de transactions.

PROBLÈMES POSSIBLES :
– l’utilisateur qui lance cette procédure n’a pas les privilèges adéquats (CONTROL SERVER)
– le compte de service de l’instance SQL Server n’a pas les droits en lecture/ecriture sur le répertoire de destination
– le répertoire de destination n’a pas une capacité suffisante pour y stocker les fichiers à déplacer
Рle r̩pertoire est un r̩pertoire mapp̩ ou un disque amovible
– la base compte du stockage FILESTREAM ou des FILETABLE

REMÈDE :
En cas d’erreur, le résultat (paneau messages de SSMS) fournit la commande qui est partie en exception, mais aussi toute la partie déjà exécutée du script de migration.

NOTA : la procédure doit être créée de préférence dans une base système, par exemple msdb ou master.

ATTENTION : l’éditeur de blog de developpez.com étant bugué, certains caractères comme ‘plus grand que » ou « plus petit que » sont supprimés, ce qui fait que le code présenté est faux.
Vous trouverez le code correcte pour cette procédure à l’URL :
http://ms.sql.server.over-blog.com/proc%C3%A9dure-de-d%C3%A9placement-de-fichier-de-bases-de-donn%C3%A9es-ms-sql-server

CREATE PROCEDURE dbo.P_MOVE_DATABASE_FILES
       @DB_NAME      sysname,       -- nom de la base
       @DESTINATION  NVARCHAR(256), -- répertoire de destination
       @LOGICAL_FILE NVARCHAR(128)  -- fichier à déplacer.
AS
/******************************************************************************
* Procédure de déplacement des fichiers d'une base de données                 *
*******************************************************************************
* Fred. Brouard - http://sqlpro.developpez.com - www.sqlspot.com - 2013-08-16 *
*******************************************************************************
* Cette procédure permet de déplacer les fichier d'une base de données        *
* FONCTIONNEMENT :                                                            *
* après avoir vérifié les paramètres, la procédure exécute séquentiellement : *
* 1 - un détachement de la base après être devenu l'utilisateur unique        *
* 2 - un transfert de fichier (par la commande xp_cmdshell)                   *
* 3 - un rattachement des fichiers par CREATE DATABASE ... FOR ATTACH         *
*******************************************************************************
* PARAMÈTRES :                                                                *
* @DB_NAME :      nom de la base (doit exister)                               *
* @DESTINATION :  répertoire de destination (doit exister)                    *
* @LOGICAL_FILE : fichier(s) à déplacer                                       *              
*     ATTENTION : ce dernier paramètre peut porter sur un nom logique de      *
*                 fichier dans la base (voir name dans sys.database_files)    *
*                 ou alors, pour un déplacement générique :                   *
*                 [ALL] (crochets compris) pour tous les fichiers             *
*                 [DATA] (crochets compris) pour les fichiers de données      *
*                 [TRAN] (crochets compris) pour les fichiers du journal de   *
*                 transactions                                                *
*******************************************************************************
* PROBLÈMES POSSIBLES :                                                       *
* - l'utilisateur qui lance cette procédure n'a pas les privilèges adéquats   *
* - le compte de service de l'instance SQL Server n'a pas les droits en       *
*   lecture/ecriture sur le répertoire de destination                         *
* - le répertoire de destination n'a pas une capacité suffisante pour y       *
*   stocker les fichiers à déplacer                                           *
* - la base compte du stockage FILESTREAM ou des FILETABLE                    *
* REMÈDE :                                                                    *
* en cas d'erreur, le résultat (paneau messages de SSMS) fournit la commande  *
* qui est partie en exception, mais aussi toute la partie déjà exécutée du    *
* script de migration.                                                        *
******************************************************************************/
BEGIN
-- variables locales
DECLARE @T TABLE (existe BIT, repertoire BIT, parent BIT);
DECLARE @F TABLE (emplacement  NVARCHAR(384),
                  destination  NVARCHAR(384));
DECLARE @SQL NVARCHAR(max), @SCRIPT NVARCHAR(max), @ERROR NVARCHAR(102);

-- vérifications diverses :
-- la base de données existe t-elle ?
IF NOT EXISTS(SELECT * FROM sys.databases WHERE name = @DB_NAME)
BEGIN  
   RAISERROR('Base de données %s inexistante.', 16, 1, @DB_NAME);
   RETURN;
END
-- est-ce une base système ?
IF @DB_NAME IN ('master', 'msdb', 'tempdb', 'model')
BEGIN  
   RAISERROR('Il n''est pas possible de déplacer les fichiers
              d''une base de données système (%s).', 16, 1);
   RETURN;
END;  
-- la destination est-elle bien un répertoire ?
IF RIGHT(@DESTINATION, 1)  '\'
   SET @DESTINATION = @DESTINATION + N'\';
SET @SQL = 'EXEC sys.xp_fileexist ''' + @DESTINATION +''';'
INSERT INTO @T EXEC (@SQL);
IF NOT EXISTS(SELECT * FROM @T WHERE repertoire = 1)
BEGIN
   RAISERROR('L''emplacement %s n''est pas un répertoire de données.',
             16, 1, @DESTINATION);
   RETURN;
END;  
-- le fichier concerné existe t-il ou s'agit t-il d'un générique ?
IF @LOGICAL_FILE NOT IN ('[ALL]', '[DATA]', '[TRAN]')
   IF NOT exists(SELECT * FROM sys.master_files
                 WHERE  DB_NAME(database_id) = @DB_NAME
                   AND  name = @LOGICAL_FILE)
   BEGIN
      RAISERROR('Le fichier logique spécifié %s pour déplacement n''est ni
                 un générique ni un fichier particulier de cette base.',
                16, 1, @LOGICAL_FILE);
      RETURN;
   END;
-- la procédure xp_cmdshell est-elle utilisable ?
IF NOT EXISTS(SELECT * FROM sys.configurations
              WHERE name = 'xp_cmdshell' AND value_in_use = 1);
BEGIN
   RAISERROR('La procédure xp_cmdshell nécessaire à l''utilisation
             de cette procédure est désactivée. Veuillez contacter
             votre adminisrateur de bases de données', 16, 1);
   RETURN;
END;              
   
-- on stocke les matadonnées du déplacement dans @F
INSERT INTO @F
SELECT physical_name,
       CASE
          WHEN @LOGICAL_FILE = '[ALL]' THEN @DESTINATION
          WHEN @LOGICAL_FILE = '[DATA]'
               AND  "type" = 0         THEN @DESTINATION
          WHEN @LOGICAL_FILE = '[TRAN]'
               AND  "type" = 1         THEN @DESTINATION
          WHEN name = @LOGICAL_FILE    THEN @DESTINATION
          ELSE NULL    
       END + REVERSE(SUBSTRING(REVERSE(physical_name), 1 ,  
                               CHARINDEX('\', REVERSE(physical_name))  -1))
FROM   sys.master_files
 WHERE  DB_NAME(database_id) = @DB_NAME;

-- construction du script de déplacement et exécution par bribes
SELECT @SCRIPT = N'';
BEGIN TRY

   -- on se met dans le contexte de la base visée
   SET @SQL = N'USE [' + @DB_NAME +'];';
   EXEC (@SQL);
   SET @SCRIPT = @SCRIPT + @SQL + NCHAR(13) + NCHAR(10)
               + N'GO' + NCHAR(13) + NCHAR(10);

   -- place la base en utilisateur unique et déconnexion des autres
   SET @SQL = N'ALTER DATABASE [' + @DB_NAME
            + N'] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;'
   EXEC (@SQL);                
   SET @SCRIPT = @SCRIPT + @SQL + NCHAR(13) + NCHAR(10)
               + N'GO' + NCHAR(13) + NCHAR(10);

   -- on se met dans le contexte de la base mster
   SET @SQL = N'USE master;';
   EXEC (@SQL);
   SET @SCRIPT = @SCRIPT + @SQL + NCHAR(13) + NCHAR(10)
               + N'GO' + NCHAR(13) + NCHAR(10);
               
   -- on détache la abse de données          
   SET @SQL = N'EXEC sp_detach_db ''' + @DB_NAME +N''';';
   EXEC (@SQL);
   SET @SCRIPT = @SCRIPT + @SQL + NCHAR(13) + NCHAR(10)
               + N'GO' + NCHAR(13) + NCHAR(10);              

   -- on déplace le(s) fichier(s) concerné(s)
   SET @SQL = N'';
   SELECT @SQL = @SQL + N'EXEC xp_cmdshell ''MOVE "' + emplacement
                                + N'" "' + destination + N'"'';'
                                + NCHAR(13) + NCHAR(10)
                                + NCHAR(13) + NCHAR(10)
   FROM   @F
   WHERE  destination IS NOT NULL
     AND  emplacement  destination;
   EXEC (@SQL);
   SET @SCRIPT = @SCRIPT + @SQL + NCHAR(13) + NCHAR(10)
               + N'GO' + NCHAR(13) + NCHAR(10);

   -- on rattache la base
   SET @SQL = N'CREATE DATABASE[' + @DB_NAME +'] ON ';
   SELECT @SQL = @SQL + NCHAR(13) + NCHAR(10)
               + N'(FILENAME = N''' + COALESCE(destination, emplacement) +'''),'  
   FROM   @F;
   SET @SQL = SUBSTRING(@SQL, 1, LEN(@SQL) -1)
            + NCHAR(13) + NCHAR(10) + ' FOR ATTACH;';
   EXEC (@SQL);        
   SET @SCRIPT = @SCRIPT + @SQL + NCHAR(13) + NCHAR(10)
               + N'GO' + NCHAR(13) + NCHAR(10);

END TRY
-- en cas d'ereur...
BEGIN CATCH
   SET @ERROR = ERROR_MESSAGE();
   RAISERROR('Une erreur est survenur lors de la procédure dbo.P_MOVE_DATABASE_FILES.', 16, 1);
-- renseigne sur la commande ayant levée l'exception
   RAISERROR('L''ereur est survenue lors de la commande : %s', 16, 1, @SQL);
-- renvoie la partie du script déjà exécutée
   RAISERROR(@ERROR, 16, 1);
   PRINT '--- PARTIE DU SCRIPT DÉJÀ EXÉCUTÉE ---';
   PRINT @SCRIPT;  
END CATCH
END;
GO

EXEMPLES :

-- déplacement de tous les fichiers composant la base :
EXEC msdb.dbo.P_MOVE_DATABASE_FILES 'DB_TEST', 'C:\db_sql\', '[ALL]'

-- déplacement des fichiers du journal de transactions :
EXEC msdb.dbo.P_MOVE_DATABASE_FILES 'DB_TEST', 'C:\db_sql\', '[TRAN]'

-- déplacement des fichiers de données :
EXEC msdb.dbo.P_MOVE_DATABASE_FILES 'DB_TEST', 'C:\db_sql\', '[DATA]'

-- déplacement d'un fichier identifié par son nom logique :
EXEC msdb.dbo.P_MOVE_DATABASE_FILES 'DB_TEST', 'C:\db_sql\', 'DB_TEST'

Le site web sur le SQL et les SGBDR
MVP Microsoft SQL Server


Frédéric Brouard, alias SQLpro, ARCHITECTE DE DONNÉES
Expert  S.G.B.D  relationnelles   et   langage  S.Q.L
Moste  Valuable  Professionnal  Microsoft  SQL Server
Société SQLspot  :  modélisation, conseil, formation,
optimisation,  audit,  tuning,  administration  SGBDR
Enseignant: CNAM PACA, ISEN Toulon, CESI Aix en Prov.

L’ntreprise SQL Spot

2 réflexions au sujet de « Procédure de déplacement de fichier de bases de données MS SQL Server »

  1. Avatar de stdebordeaustdebordeau

    Bonjour,
    J’ai essayé votre procédure, mais j’ai commis une grosse bourde. J’ai déplacé un mauvais fichier .ldf vers le bon situé sur autre répertoire et portant le même nom. Il me semble qu’il l’a écrasé. Maintenant je ne peux plus joindre mon fichier .mdf.

    A ce point j’ai juste besoin de récupérer les procédures stockés que j’y avais écrite. Y’a t’il des possibilités ? Merci d’avance

Laisser un commentaire