这是我执行以恢复远程 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 中手动执行它,它就可以工作。
看起来数据库中有多个数据文件,但您没有考虑到它们都需要具有唯一的物理文件名。 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