PowerShell 数据库还原问题,mdf 文件被另一个文件声明。 WITH MOVE 子句可用于重新定位一个或多个文件

问题描述 投票:0回答:1

这是我执行以恢复远程 SQL Server 实例上的数据库的脚本:

    param(
    [Parameter(Mandatory, HelpMessage="Enter the SQL Server hostname. I.e 'plwron164\sql2014'")]
    [string]$sqlServerLocation,
    [Parameter(Mandatory, HelpMessage="Enter the location id custom database name")]
    [string]$locationId,
    [Parameter(Mandatory, HelpMessage="Enter the path to the database backup file. Must be physical path to backup on server!")]
    [string]$backupPath
)
Import-Module .\modules\GenerateEnvironment\gecommon.psd1 -Force
InstallSqlServerModule
$newDbName = "DATABASE`_$locationId"
try {
    RemoveDbIfExists -sqlServerLocation $sqlServerLocation -locationId $locationId
    $server = New-Object Microsoft.SqlServer.Management.Smo.Server $sqlServerLocation;
    $dbFiles = Invoke-Sqlcmd -ServerInstance $sqlServerLocation -Query "RESTORE FILELISTONLY FROM DISK='$backupPath';" -TrustServerCertificate
    $relocate = @()
    foreach($dbFile in $dbFiles) 
    {
        if($dbFile.Type -eq 'L') 
        {
            $newfile = (Join-Path -Path $server.Settings.DefaultLog -ChildPath $newDbName) + '_log.ldf'
        } 
        else 
        {
            $newfile = (Join-Path -Path $server.Settings.DefaultFile -ChildPath $newDbName) + '.mdf'
        }
        
        $relocate += New-Object Microsoft.SqlServer.Management.Smo.RelocateFile ($dbFile.LogicalName,$newfile)
    }

    WriteLog("Trying restore DB in $sqlServerLocation server with database name $newDbName...")
    Restore-SqlDatabase -ServerInstance $sqlServerLocation -Database $newDbName -BackupFile $backupPath -PassThru -RelocateFile $relocate
    WriteLog("Successfully restored DB") 
    WriteLog("Add for test user access to this DB")
    AddPermissionsForUser -sqlServerLocation $sqlServerLocation -locationId $locationId -user "USER_Test"

}
catch {
    WriteErrorLog("Error with restore db. Exception: $_")
    throw
}

并用线:

Restore-SqlDatabase -ServerInstance $sqlServerLocation -Database $newDbName -BackupFile $backupPath -PassThru -RelocateFile $relocate

每次当文件被另一个文件声明且无法重新定位时,我都会收到此错误:

Restore-SqlDatabase:Microsoft.Data.SqlClient.SqlError:文件“I:\SQL2017\MSSQL14.SQL2017\MSSQL\DATA\DATABASE.mdf”由“DATABASE1”(3) 声明,并且 “数据库2”(1)。 WITH MOVE 子句可用于重新定位一个或多个文件。

如何编辑 PowerShell 脚本以重新定位另一个文件声明的文件? 如果我在 SSMS 中手动执行它,它就可以工作。

sql-server database powershell powershell-2.0
1个回答
0
投票

看起来数据库中有多个数据文件,但您没有考虑到它们都需要具有唯一的物理文件名。 SSMS 会自动为您处理这个问题,这就是它在那里工作的原因。

作为示例,我创建了一个名为 Database 的数据库,如下所示:

CREATE DATABASE [database]
 CONTAINMENT = NONE
 ON  PRIMARY 
( NAME = N'database', FILENAME = N'D:\Data\database.mdf' , SIZE = 8192KB , MAXSIZE = UNLIMITED, FILEGROWTH = 65536KB ),
( NAME = N'database2', FILENAME = N'D:\Data\database2.ndf' , SIZE = 8192KB , MAXSIZE = UNLIMITED, FILEGROWTH = 65536KB ),
( NAME = N'database3', FILENAME = N'D:\Data\database3.ndf' , SIZE = 8192KB , MAXSIZE = UNLIMITED, FILEGROWTH = 65536KB )
 LOG ON 
( NAME = N'database_log', FILENAME = N'F:\Log\database_log.ldf' , SIZE = 8192KB , MAXSIZE = 2048GB , FILEGROWTH = 65536KB )
 WITH CATALOG_COLLATION = DATABASE_DEFAULT, LEDGER = OFF

你可以看到我有3个数据文件。使用您的脚本,$dbFiles 由 RESTORE FILELISTONLY 填充,如果我查看 $dbFiles,我可以看到这些独特的文件:

LogicalName  PhysicalName            Type FileGroupName
-----------  ------------            ---- -------------
database     D:\Data\database.mdf    D    PRIMARY
database2    D:\Data\database2.ndf   D    PRIMARY
database3    D:\Data\database3.ndf   D    PRIMARY
database_log F:\Log\database_log.ldf L

您的 foreach 循环不考虑多个数据文件,因此 $relocate 保留唯一的逻辑名称,但数据文件现在都是完全相同的路径,这就是您收到“已声明”消息的原因:

LogicalFileName PhysicalFileName
--------------- ----------------
database        D:\Data\DATABASE_2.mdf
database2       D:\Data\DATABASE_2.mdf
database3       D:\Data\DATABASE_2.mdf
database_log    F:\Log\DATABASE_2_log.ldf

作为快速修复,您可以将 foreach 部分更新为类似这样的内容,其中每次迭代都会增加一个数字值以包含在文件名中(这也使用源文件扩展名,而不是调用所有内容 .mdf)。

$relocate = @()
$l = 0 #log file number
$d = 0 #data file number
foreach($dbFile in $dbFiles) 
    {
        if($dbFile.Type -eq 'L') 
        {
            $l += 1
            $newfile = (Join-Path -Path $server.Settings.DefaultLog -ChildPath $newDbName) + '_log_' + $l + (split-path $dbfile.physicalname -extension)
        } 
        else 
        {
            $d += 1
            $newfile = (Join-Path -Path $server.Settings.DefaultFile -ChildPath $newDbName) + '_' + $d + (split-path $dbfile.physicalname -extension)
        }
        
        $relocate += New-Object Microsoft.SqlServer.Management.Smo.RelocateFile ($dbFile.LogicalName,$newfile)
    }

我现在又有了独特的名字。它们与原始版本并不完美匹配,因为这只是一个快速修复,但它至少提供了唯一的文件名,使恢复能够成功。如果您想更多地匹配原始内容,您可以添加更多逻辑来实现这一点。

LogicalFileName PhysicalFileName
--------------- ----------------
database        D:\Data\DATABASE_2_1.mdf
database2       D:\Data\DATABASE_2_2.ndf
database3       D:\Data\DATABASE_2_3.ndf
database_log    F:\Log\DATABASE_2_log_1.ldf
© www.soinside.com 2019 - 2024. All rights reserved.