Bio*_*eek 17 sql-server sql-server-2008-r2 alter-database
我的 SQL Server 2008 R2 有 323 个数据库,在我的 C: 驱动器上消耗了大约 14 GB,这是一个快速的 SSD。
因为我想回收 C: 驱动器上的一些空间,所以我想将它们移动到我的 D: 驱动器。
我找到了这篇 MSDN 文章,但这似乎是只移动一个数据库的过程。
是否有自动方式或脚本一次移动我的所有数据库?
Mik*_*Fal 20
我使用 Powershell 进行此类工作。事实上,我使用 Powershell 来生成 Powershell,因为我有一个脚本可以循环遍历我的数据库并生成我的最终移动脚本。您必须一次移动一个数据库,但这至少可以帮助您编写 90% 的工作。
#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
}
}
Run Code Online (Sandbox Code Playgroud)
输出将是 MyDocuments 文件夹中的 FileMover.ps1 脚本,如下所示:
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"
...
Run Code Online (Sandbox Code Playgroud)
注意事项
您可以使用 Alter database Modify File 或 Detach/Attach 方法。
注意:两者都需要一些停机时间,因此必须在维护窗口期间完成。
这假设您在新驱动器上具有相同的目录结构,例如 C:\data\ 和 D:\Data。
-- 将 Alter 数据库与 Modify 方法一起使用(首选)
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
Run Code Online (Sandbox Code Playgroud)
--- 使用旧的分离/附加方法(不是首选,但仍然有人使用它。不幸的是,我最近在非生产服务器上使用了它)。
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
Run Code Online (Sandbox Code Playgroud)
我所知道的一次执行多个数据库的唯一方法是一次为多个数据库编写移动脚本。
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;
-------
Run Code Online (Sandbox Code Playgroud)
在这里,您可以手动移动文件,也可以编写脚本来执行此操作。可能使用 xp_cmdshell 或一些工具。不过,手动移动文件可能更容易。标记一堆,然后拖放。
-------
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;
Run Code Online (Sandbox Code Playgroud)
当然,如果您移动数据文件和日志文件,您必须确保对每个部分都进行了 MODIFY FILE 部分。
归档时间: |
|
查看次数: |
12008 次 |
最近记录: |