一次将多个数据库从 C: 移动到 D:

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)

注意事项

  • 该脚本将所有文件(无论其源位置如何)移动到同一目的地。您将需要调整自定义位置路径。
  • 该脚本旨在在您需要移动文件的服务器上运行(请参阅 localhost' 的所有用法)。如果您远程运行,请将 localhost 替换为您的实例名称。
  • 运行此操作的用户需要访问移动中涉及的所有文件夹路径,用于更新 SQL 服务器文件名信息和移动文件。
  • 由于 .SetOffline() 和 .SetOnline 方法的时髦特性,我使用 InvokeSQLCmd 进行离线/在线执行。我发现这更可靠。


Kin*_*hah 7

您可以使用 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)


Ken*_*her 6

我所知道的一次执行多个数据库的唯一方法是一次为多个数据库编写移动脚本。

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 部分。