Yo uso Powershell para este tipo de trabajo. De hecho, uso Powershell para generar Powershell, porque tengo un script que recorrerá mis bases de datos y generará mi script de movimiento final. Tendrá que mover cada base de datos de una en una, pero esto al menos le ayudará a escribir el 90% del trabajo.
#load SMO
Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
#Added line ifusing SQL Server 2012or 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($filein$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 `"ALTERDATABASE["+$db_build.Name+"]SET OFFLINE WITHROLLBACK IMMEDIATE;`" -Database `"master`""$dbchange >>$outputfile
$robocpy >>$outputfile
"Invoke-Sqlcmd -Query `"ALTERDATABASE["+$db_build.Name+"]SET ONLINE;`" -Database `"master`"">>$outputfile
}}
El resultado será un script FileMover.ps1 en su carpeta MyDocuments que se verá así:
El script mueve todos los archivos, sin importar su ubicación de origen, al mismo destino. Deberá ajustar las rutas de ubicación personalizadas.
El script está diseñado para ejecutarse en el servidor en el que necesita mover los archivos (ver todos los usos de localhost '). Reemplace localhost con su nombre de instancia si ejecuta esto de forma remota.
El usuario con el que ejecuta esto necesita acceso a todas las rutas de carpetas involucradas en el movimiento, tanto para actualizar la información del nombre de archivo del servidor SQL como para mover los archivos.
Utilizo InvokeSQLCmd para la ejecución fuera de línea / en línea debido a la naturaleza funky de los métodos .SetOffline () y .SetOnline. He encontrado que esto es más confiable.
@ MikeFal Vi que aprobaste la edición. Dado que la pregunta está etiquetada con 2008R2, ¿no debería ser más evidente la adición (negrita o algo así)? (No tengo idea, pero supongo que podría no funcionar o romper algo en una versión que no sea 2012).
ypercubeᵀᴹ
1
Pensé en eso y lo ejecuté en SQL Server 2012 R2: arroja errores con los cmdlets Add-PSSnapin SqlServerCmdletSnapin100, pero procesa siempre que tenga el SQLPS Import-Module incluido antes de la parte principal del script El proceso se ejecuta. Técnicamente, esto debería tener un mejor error al verificar esto, pero pensé que sería una buena edición rápida para ayudar a alguien que tal vez no pudo encontrar la necesidad del SQLPS Import-Module si están en un Versión más nueva.
Chad Rexin
1
Gracias una tonelada. Pequeño problema sin embargo. Los nombres de los archivos de robocopy no se citan aquí. Si el usuario ha creado nombres de bases de datos con espacios, no funciona del todo bien.
Tim Brigham el
7
Puede utilizar los métodos Modificar archivo de base de datos o Separar / Adjuntar.
Nota: Ambos requerirán algún tiempo de inactividad, por lo que debe hacerse durante la ventana de mantenimiento.
Esto supone que tiene la misma estructura de directorio en la nueva unidad, por ejemplo, C: \ data \ y D: \ Data.
- utilizando la base de datos Alter con el método Modify (preferido)
SET NOCOUNT ONDECLARE@datafile VARCHAR(255),@logfile VARCHAR(255),@dbid TINYINT
,@SQLText VARCHAR(max),@dbname VARCHAR(255),@sqltext1 VARCHAR(max),@SQLText2 VARCHAR(max)--2. Prepare for modifyIFEXISTS(SELECT1FROM tempdb..sysobjects
WHERE NAME LIKE'%#filetable%')BEGINDROPTABLE#filetable
ENDCREATETABLE#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 =1INSERT#filetable (
ldf
,dbid
,fileid
,logicalname
)SELECT physical_name
,database_id
,data_space_id
,NAME
FROM sys.master_files
WHERE data_space_id =0UPDATE u
SET u.dbname = s.NAME
FROM#filetable u
INNERJOIN 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 >4WHILE@dbid ISNOTNULLBEGINSELECT@SQLText ='alter database ['+ dbname +'] MODIFY FILE (Name = '+ logicalname +' , FileName = N'''+ ldf +''');'FROM#filetable
WHERE dbid =convert(VARCHAR,@dbid)AND fileid =0-- Log filePRINT@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 filePRINT@SQLText2
--Exec(@SQLText)SELECT@dbid = min(dbid)FROM#filetable
WHERE dbid >4AND dbid >@dbid
END
--- usando el método Old Detach / Attach (no preferido, pero la gente todavía lo usa ... desafortunadamente lo usé recientemente en un servidor que no es de producción).
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 AttachIFEXISTS(SELECT1FROM tempdb..sysobjects
WHERE NAME LIKE'%#filetable%')BEGINDROPTABLE#filetable
ENDCREATETABLE#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 =1INSERT#filetable (
ldf
,dbid
,fileid
)SELECT physical_name
,database_id
,data_space_id
FROM sys.master_files
WHERE data_space_id =0UPDATE u
SET u.dbname = s.NAME
FROM#filetable u
INNERJOIN 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 >4WHILE@dbid ISNOTNULLBEGINSELECT@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 ISNOTNULLSELECT@SQLText2 =@SQLText2 +','''+ ldf +''''FROM#filetable
WHERE dbid =@dbid
AND ldf ISNOTNULLPRINT@SQLText2
--Exec(@SQLText)SELECT@dbid = min(dbid)FROM#filetable
WHERE dbid >4AND dbid >@dbid
ENDDROPTABLE#filetable
La única forma en que sé hacer múltiples bases de datos a la vez sería escribir el movimiento para múltiples bases de datos a la vez.
ALTERDATABASE database_nameA SET OFFLINE WITHROLLBACK IMMEDIATE;ALTERDATABASE database_nameB SET OFFLINE WITHROLLBACK IMMEDIATE;ALTERDATABASE database_nameC SET OFFLINE WITHROLLBACK IMMEDIATE;-------
Aquí puede mover los archivos manualmente o escribir un script para hacerlo. Posiblemente usando xp_cmdshell o alguna herramienta. Sin embargo, probablemente sea más fácil mover los archivos a mano. Marque un montón de ellos, luego arrastre y suelte.
-------ALTERDATABASE database_nameA MODIFY FILE( NAME = logical_name, FILENAME ='new_path\os_file_name');ALTERDATABASE database_nameB MODIFY FILE( NAME = logical_name, FILENAME ='new_path\os_file_name');ALTERDATABASE database_nameC MODIFY FILE( NAME = logical_name, FILENAME ='new_path\os_file_name');ALTERDATABASE database_nameA SET ONLINE;ALTERDATABASE database_nameB SET ONLINE;ALTERDATABASE database_nameC SET ONLINE;
Por supuesto, si mueve el archivo de datos y el archivo de registro, debe asegurarse de hacer la parte MODIFICAR ARCHIVO para cada parte.
Este script devolverá un lote de declaraciones que puede ejecutar.
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
innerjoin sys.databases d on d.database_id=f.database_id
Puede utilizar los métodos Modificar archivo de base de datos o Separar / Adjuntar.
Nota: Ambos requerirán algún tiempo de inactividad, por lo que debe hacerse durante la ventana de mantenimiento.
Esto supone que tiene la misma estructura de directorio en la nueva unidad, por ejemplo, C: \ data \ y D: \ Data.
- utilizando la base de datos Alter con el método Modify (preferido)
--- usando el método Old Detach / Attach (no preferido, pero la gente todavía lo usa ... desafortunadamente lo usé recientemente en un servidor que no es de producción).
fuente
La única forma en que sé hacer múltiples bases de datos a la vez sería escribir el movimiento para múltiples bases de datos a la vez.
Aquí puede mover los archivos manualmente o escribir un script para hacerlo. Posiblemente usando xp_cmdshell o alguna herramienta. Sin embargo, probablemente sea más fácil mover los archivos a mano. Marque un montón de ellos, luego arrastre y suelte.
Por supuesto, si mueve el archivo de datos y el archivo de registro, debe asegurarse de hacer la parte MODIFICAR ARCHIVO para cada parte.
fuente
fuente
Este script devolverá un lote de declaraciones que puede ejecutar.
fuente