J'ai SQL Server 2008 R2 avec 323 bases de données consommant environ 14 Go sur mon lecteur C:, un SSD rapide.
Parce que je veux récupérer de l'espace sur mon lecteur C:, je voudrais les déplacer vers mon lecteur D :.
J'ai trouvé cet article MSDN , mais cela semble être la procédure pour déplacer une seule base de données.
Existe-t-il un moyen automatique ou un script pour déplacer toutes mes bases de données en même temps?
J'utilise Powershell pour ce genre de travail. En fait, j'utilise Powershell pour générer Powershell, car j'ai un script qui va parcourir mes bases de données et générer mon script de déplacement final. Vous devrez déplacer chaque base de données une par une, mais cela vous aidera au moins à scénariser 90% du travail.
#load SMO
Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
#Added line if using SQL Server 2012 or later
Import-module SQLPS
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
#Create server object and output filename
$server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server "localhost"
$outputfile=([Environment]::GetFolderPath("MyDocuments"))+"\FileMover.ps1"
#set this for your new location
$newloc="X:\NewDBLocation"
#get your databases
$db_list=$server.Databases
#build initial script components
"Add-PSSnapin SqlServerCmdletSnapin100" > $outputfile
"Add-PSSnapin SqlServerProviderSnapin100" >> $outputfile
"Import-Module SQLPS" >> $outputfile
"[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') `"localhost`" | out-null" >> $outputfile
"`$server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server " >> $outputfile
foreach($db_build in $db_list)
{
#only process user databases
if(!($db_build.IsSystemObject))
{
#script out all the file moves
"#----------------------------------------------------------------------" >> $outputfile
"`$db=`$server.Databases[`""+$db_build.Name+"`"]" >> $outputfile
$dbchange = @()
$robocpy =@()
foreach ($fg in $db_build.Filegroups)
{
foreach($file in $fg.Files)
{
$shortfile=$file.Filename.Substring($file.Filename.LastIndexOf('\')+1)
$oldloc=$file.Filename.Substring(0,$file.Filename.LastIndexOf('\'))
$dbchange+="`$db.FileGroups[`""+$fg.Name+"`"].Files[`""+$file.Name+"`"].Filename=`"$newloc`\"+$shortfile+"`""
$robocpy+="ROBOCOPY `"$oldloc`" `"$newloc`" $shortfile /copyall /mov"
}
}
foreach($logfile in $db_build.LogFiles)
{
$shortfile=$logfile.Filename.Substring($logfile.Filename.LastIndexOf('\')+1)
$oldloc=$logfile.Filename.Substring(0,$logfile.Filename.LastIndexOf('\'))
$dbchange+="`$db.LogFiles[`""+$logfile.Name+"`"].Filename=`"$newloc`\"+$shortfile+"`""
$robocpy+="ROBOCOPY `"$oldloc`" `"$newloc`" $shortfile /copyall /mov"
}
$dbchange+="`$db.Alter()"
$dbchange+="Invoke-Sqlcmd -Query `"ALTER DATABASE ["+$db_build.Name+"] SET OFFLINE WITH ROLLBACK IMMEDIATE;`" -Database `"master`""
$dbchange >> $outputfile
$robocpy >> $outputfile
"Invoke-Sqlcmd -Query `"ALTER DATABASE ["+$db_build.Name+"] SET ONLINE;`" -Database `"master`"" >> $outputfile
}
}
La sortie sera un script FileMover.ps1 dans votre dossier MyDocuments qui ressemble à ceci:
Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
Import-Module SQLPS
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') "localhost" | out-null
$server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server
#----------------------------------------------------------------------
$db=$server.Databases["AdventureWorks2012"]
$db.FileGroups["PRIMARY"].Files["AdventureWorks2012_Data"].Filename="X:\NewDBLocation\AdventureWorks2012_Data.mdf"
$db.LogFiles["AdventureWorks2012_Log"].Filename="X:\NewDBLocation\AdventureWorks2012_log.ldf"
$db.Alter()
Invoke-Sqlcmd -Query "ALTER DATABASE [AdventureWorks2012] SET OFFLINE WITH ROLLBACK IMMEDIATE;" -Database "master"
ROBOCOPY "C:\DBData" "X:\NewDBLocation" AdventureWorks2012_Data.mdf /copyall /mov
ROBOCOPY "C:\DBFiles\Log" "X:\NewDBLocation" AdventureWorks2012_log.ldf /copyall /mov
Invoke-Sqlcmd -Query "ALTER DATABASE [AdventureWorks2012] SET ONLINE;" -Database "master"
#----------------------------------------------------------------------
$db=$server.Databases["AdventureWorks2012DW"]
$db.FileGroups["PRIMARY"].Files["AdventureWorksDW2012_Data"].Filename="X:\NewDBLocation\AdventureWorksDW2012_Data.mdf"
$db.LogFiles["AdventureWorksDW2012_Log"].Filename="X:\NewDBLocation\AdventureWorks2012DW_log.ldf"
$db.Alter()
Invoke-Sqlcmd -Query "ALTER DATABASE [AdventureWorks2012DW] SET OFFLINE WITH ROLLBACK IMMEDIATE;" -Database "master"
ROBOCOPY "C:\DBData" "X:\NewDBLocation" AdventureWorksDW2012_Data.mdf /copyall /mov
ROBOCOPY "C:\DBData" "X:\NewDBLocation" AdventureWorks2012DW_log.ldf /copyall /mov
Invoke-Sqlcmd -Query "ALTER DATABASE [AdventureWorks2012DW] SET ONLINE;" -Database "master"
...
Avertissements
Vous pouvez utiliser les méthodes Modifier la base de données Modifier le fichier ou Détacher/Attacher.
Remarque: Les deux nécessiteront un certain temps d'arrêt, donc doit être fait pendant la fenêtre de maintenance.
Cela suppose que vous avez la même structure de répertoires sur le nouveau lecteur, par ex. C:\data\et D:\Data.
- utilisation de la base de données Alter avec la méthode Modify (préférée)
SET NOCOUNT ON
DECLARE @datafile VARCHAR(255)
,@logfile VARCHAR(255)
,@dbid TINYINT
,@SQLText VARCHAR(max)
,@dbname VARCHAR(255)
,@sqltext1 VARCHAR(max)
,@SQLText2 VARCHAR(max)
--2. Prepare for modify
IF EXISTS (
SELECT 1
FROM tempdb..sysobjects
WHERE NAME LIKE '%#filetable%'
)
BEGIN
DROP TABLE #filetable
END
CREATE TABLE #filetable (
mdf VARCHAR(255)
,ldf VARCHAR(255)
,dbid TINYINT
,dbname VARCHAR(100)
,fileid TINYINT
,logicalname SYSNAME
)
--
INSERT #filetable (
mdf
,dbid
,fileid
,logicalname
)
SELECT physical_name
,database_id
,data_space_id
,NAME
FROM sys.master_files
WHERE data_space_id = 1
INSERT #filetable (
ldf
,dbid
,fileid
,logicalname
)
SELECT physical_name
,database_id
,data_space_id
,NAME
FROM sys.master_files
WHERE data_space_id = 0
UPDATE u
SET u.dbname = s.NAME
FROM #filetable u
INNER JOIN master..sysdatabases s ON u.dbid = s.dbid
UPDATE #filetable
SET mdf = replace(mdf, 'C:', 'D:')
,ldf = replace(ldf, 'C:', 'D:')
FROM #filetable
SELECT @dbid = min(dbid)
FROM #filetable
WHERE dbid > 4
WHILE @dbid IS NOT NULL
BEGIN
SELECT @SQLText = 'alter database [' + dbname + '] MODIFY FILE (Name = ' + logicalname + ' , FileName = N''' + ldf + ''');'
FROM #filetable
WHERE dbid = convert(VARCHAR, @dbid)
AND fileid = 0 -- Log file
PRINT @SQLText
--Exec(@SQLText)
SELECT @SQLText2 = 'alter database [' + dbname + '] MODIFY FILE (Name = ' + logicalname + ' , FileName = N''' + mdf + ''');'
FROM #filetable
WHERE dbid = convert(VARCHAR, @dbid)
AND fileid = 1 -- data file
PRINT @SQLText2
--Exec(@SQLText)
SELECT @dbid = min(dbid)
FROM #filetable
WHERE dbid > 4
AND dbid > @dbid
END
--- en utilisant la méthode Old Detach/Attach (pas préféré, mais les gens l'utilisent encore .. malheureusement je l'ai utilisé récemment sur un serveur NON prod).
DECLARE @datafile VARCHAR(255)
,@logfile VARCHAR(255)
,@dbid TINYINT
,@SQLText VARCHAR(8000)
,@dbname VARCHAR(255)
,@SQLText2 VARCHAR(8000)
--2. Detach All Local Databases and prepare for Attach
IF EXISTS (
SELECT 1
FROM tempdb..sysobjects
WHERE NAME LIKE '%#filetable%'
)
BEGIN
DROP TABLE #filetable
END
CREATE TABLE #filetable (
mdf VARCHAR(255)
,ldf VARCHAR(255)
,dbid TINYINT
,dbname VARCHAR(100)
,fileid TINYINT
)
--
INSERT #filetable (
mdf
,dbid
,fileid
)
SELECT physical_name
,database_id
,data_space_id
FROM sys.master_files
WHERE data_space_id = 1
INSERT #filetable (
ldf
,dbid
,fileid
)
SELECT physical_name
,database_id
,data_space_id
FROM sys.master_files
WHERE data_space_id = 0
UPDATE u
SET u.dbname = s.NAME
FROM #filetable u
INNER JOIN master..sysdatabases s ON u.dbid = s.dbid
UPDATE #filetable
SET mdf = replace(mdf, 'C:', 'D:')
,ldf = replace(ldf, 'C:', 'D:')
FROM #filetable
SELECT @dbid = min(dbid)
FROM #filetable
WHERE dbid > 4
WHILE @dbid IS NOT NULL
BEGIN
SELECT @SQLText = 'alter database [' + dbname + ']'
FROM #filetable
WHERE dbid = convert(VARCHAR, @dbid)
SELECT @SQLText = @SQLText + CHAR(10) + ' set single_user with rollback immediate;'
SELECT @SQLText = @SQLText + CHAR(10) + ' exec master..sp_detach_db ' + dbname
FROM #filetable
WHERE dbid = convert(VARCHAR, @dbid)
PRINT @SQLText
--Exec(@SQLText)
SELECT @SQLText2 = 'exec master..sp_attach_db ''' + dbname + ''''
FROM #filetable
WHERE dbid = @dbid
SELECT @SQLText2 = @SQLText2 + ',''' + mdf + ''''
FROM #filetable
WHERE dbid = @dbid
AND mdf IS NOT NULL
SELECT @SQLText2 = @SQLText2 + ',''' + ldf + ''''
FROM #filetable
WHERE dbid = @dbid
AND ldf IS NOT NULL
PRINT @SQLText2
--Exec(@SQLText)
SELECT @dbid = min(dbid)
FROM #filetable
WHERE dbid > 4
AND dbid > @dbid
END
DROP TABLE #filetable
La seule façon que je sache de faire plusieurs bases de données à la fois serait de scripter le déplacement de plusieurs bases de données à la fois.
ALTER DATABASE database_nameA SET OFFLINE WITH ROLLBACK IMMEDIATE;
ALTER DATABASE database_nameB SET OFFLINE WITH ROLLBACK IMMEDIATE;
ALTER DATABASE database_nameC SET OFFLINE WITH ROLLBACK IMMEDIATE;
-------
Ici, vous pouvez soit déplacer les fichiers manuellement, soit écrire un script pour le faire. Peut-être en utilisant xp_cmdshell ou un outil. Probablement plus facile de déplacer simplement les fichiers à la main. Marquez-en un tas, puis glissez-déposez.
-------
ALTER DATABASE database_nameA MODIFY FILE ( NAME = logical_name, FILENAME = 'new_path\os_file_name' );
ALTER DATABASE database_nameB MODIFY FILE ( NAME = logical_name, FILENAME = 'new_path\os_file_name' );
ALTER DATABASE database_nameC MODIFY FILE ( NAME = logical_name, FILENAME = 'new_path\os_file_name' );
ALTER DATABASE database_nameA SET ONLINE;
ALTER DATABASE database_nameB SET ONLINE;
ALTER DATABASE database_nameC SET ONLINE;
Bien sûr, si vous déplacez le fichier de données et le fichier journal, vous devez vous assurer de faire la partie MODIFY FILE pour chaque partie.
Ce script renverra un lot d'instructions que vous pouvez exécuter.
SELECT d.name as db, f.name, physical_name, f.state_desc,
'ALTER DATABASE ['+d.name+'] MODIFY FILE (name='''+f.name+''' ,filename='''+replace(physical_name,'C:\database','D:\whatever')+'''); ' as DetachCommand,
'ALTER DATABASE ['+d.name+'] SET ONLINE' as ReattachCommand
from sys.master_files f
inner join sys.databases d on d.database_id=f.database_id
------------------------------
[email protected] - July 2016
use master;
go
SET NOCOUNT ON
print '----------------------------------------------------------------------------------'
print '--Script for Moving Multiple database files to a new drive / ' + CONVERT(varchar(256),getdate() )
print '----------------------------------------------------------------------------------'
print ''
DECLARE @dbname nvarchar(128)
DECLARE @DestPath nvarchar(256)
--Set here the new destination path of the file
set @DestPath = 'T:\Data\'
------------------------------------------------
--Filter: HD Databases
------------------------------------------------
DECLARE DBList_cursor CURSOR FOR
Select name from sys.databases
--where name like '<FIlter Something>'
----------------------------------------------
OPEN DBList_cursor
FETCH NEXT FROM DBList_cursor
INTO @dbname
WHILE @@FETCH_STATUS = 0
BEGIN
declare @output_script varchar(max) --Output of the generated script
declare @mdf_orig_path nvarchar(256) --Original datbase file path
declare @cmdstring nvarchar(256) --Command String
declare @CursorDeclare varchar(max) --Cursor declaration command
declare @Originalfilename varchar(max) -- local @CursorDeclare command
declare @filename varchar(max) -- local @CursorDeclare command
declare @LogicalFileaame varchar(max) -- Logical FileName
--Set null into @output script
set @output_script=''
--Generate Databse Cursor declaration command
set @CursorDeclare='DECLARE DBFiles_cursor CURSOR FOR select [filename], [name] from '+ @dbname + '.sys.sysfiles'
--Cursor Declaration
execute (@CursorDeclare)
OPEN DBFiles_cursor
FETCH NEXT FROM DBFiles_cursor INTO @filename, @LogicalFileaame
--For RollBack Option
select @Originalfilename = @filename
--Modify Physical FileName
if (@filename like '%.mdf') begin
select @mdf_orig_path = @filename
IF(CHARINDEX('\', @filename) > 0)
select @filename = RIGHT(@filename, CHARINDEX('\', REVERSE(@filename)) -1)
select @filename = @DestPath + @filename
select @cmdstring = ' ''copy' + ' ' + '"'+ @mdf_orig_path + '"' + ' ' + '"' + @filename +'"' + ''''
--Get Logical FileNAme
end
print CHAR(10)
print '-----------------------------------------'
print @dbname
print '-----------------------------------------'
print CHAR(10)
print 'print ''Start'' + CONVERT(varchar(256), getdate() ) '
print '---Offline Database' + @dbname
print 'ALTER DATABASE ' + @dbname + ' SET OFFLINE WITH ROLLBACK IMMEDIATE' + CHAR(10) + 'GO'
print 'exec master..xp_cmdshell' + ' ' + @cmdstring + CHAR(10)
print '--For RollBack Use this:ALTER DATABASE ' + @dbname +' MODIFY FILE ( NAME =' + @LogicalFileaame +', FILENAME =' + @Originalfilename + ')' + CHAR(10)
print 'ALTER DATABASE ' + @dbname +' MODIFY FILE ( NAME =' + @LogicalFileaame +', FILENAME =' + '''' + @DestPath + @dbname + '.mdf'' )' +CHAR(10)
print '---ONline Database' + @dbname
print 'ALTER DATABASE ' + @dbname + ' SET ONLINE WITH
ROLLBACK IMMEDIATE
GO'
WHILE @@FETCH_STATUS = 0
BEGIN
set @output_script=@output_script+' (FILENAME = '''+ @filename +'''),'
FETCH NEXT FROM DBFiles_cursor INTO @filename, @LogicalFileaame
END
set @output_script=SUBSTRING(@output_script,0,len(@output_script))
CLOSE DBFiles_cursor
DEALLOCATE DBFiles_cursor
FETCH NEXT FROM DBList_cursor
INTO @dbname
END
CLOSE DBList_cursor
DEALLOCATE DBList_cursor