you*_*gme 2 sql-server backup restore sql-server-2016
我发现在生产服务器上有一个名为 MYOBInterim 的数据库。在其他环境(开发、测试)上,有一个名为 MYOBTraining 的数据库。(别问我为什么——我还在努力得到那个答案……)
我从开发实例创建了其中一个 MYOBTraining 数据库的备份,然后尝试将其恢复到另一个没有此类数据库的开发实例。
我的 SQL 大致如下。
use master;
go
If(db_id(N'MYOBTraining') IS NULL)
create database MYOBTraining
on
(name = MYOBTraining,
filename = 'D:\Database\Dev4\MYOBTraining.mdf' )
log on
(name = MYOBTraining_log,
filename = 'E:\Logs\Dev4\MYOBTraining_1.ldf' )
;
go
-- BIG NOTE:
-- Note we are moving 'MYOBInterim' (and not 'MYOBTraining')!
-- Prod calls this DB MYOBInterim. Even though we obtain the backup for use with this restore from DEV - where the DB is called MYOBTraining,
-- It seems the logical name within SQL Server is still MYOBInterim despite the DB appearing in the list with the name "MYOBTraining".
RESTORE DATABASE MYOBTraining FROM DISK = 'E:\_DBA\BackupFiles\MYOBTraining.bak' WITH FILE = 1
, MOVE 'MYOBInterim' TO 'D:\Database\Dev4\MYOBTraining.mdf'
, MOVE 'MYOBInterim_log' TO 'E:\Logs\Dev4\MYOBTraining_1.ldf'
, NOUNLOAD, REPLACE, STATS = 1
Run Code Online (Sandbox Code Playgroud)
发生的事情是最初这些MOVE
子句说MOVE 'MYOBTraining'
等,但这返回了一个错误,指出“RESTORE 失败,操作系统错误 32,文件正在被另一个进程使用”。尽管 MS 有一篇文章说这是一个已知问题(关于包含文件流数据的条带备份集),该问题已在补丁中修复,但一篇关于堆栈交换的文章建议仔细检查您使用的逻辑名称是否正确。当我再次更仔细地阅读错误消息时,它说MYOBInterim无法恢复。显然它是从备份的数据库文件中获取这个名称,因为它不存在于我用于恢复的 SQL 中,所以我的问题是 - 当它出现在开发中时,它为什么将数据库称为 MYOBInterim我在 SSMS 中作为 MYOBTraining 进行备份的服务器?
就其价值而言,我上面给出的 SQL(即,在将逻辑名称更改为 MYOBInterim 之后,如图所示)确实有效并恢复了数据库。
详细步骤
最初我没有提供完整的步骤和错误消息,因为我已经解决了问题并且不再有确切的错误可用。然而,每次讨论,这些都是需要的,所以我在下面重新创建问题。
删除了 Dev4 上的 MYOBTraining 数据库 - 将 Dev4 带回到起点 > 刷新 SSMS 中数据库文件夹的视图 - 数据库消失了。
在 SSMS 中,转到开发 > 数据库 > 右键单击 MYOBTraining > 任务 > 备份...
常规选项卡 > 源显示“MYOBTraining”,恢复模式显示完整,备份类型显示完整,备份组件选择了数据库,目标/备份到显示磁盘,目标位置显示 NUL > 选择 NUL > 删除按钮 > 添加按钮 > 文件名 = E :\Backup\MYOBTraining_20201203_Full.bak > 确定按钮
媒体选项和备份选项选项卡 - 默认没有变化
点击 OK 按钮 > 备份执行 > 出现说成功备份的对话框 > 点击 OK 按钮并关闭所有对话框
到那台机器的远程桌面 > 备份文件准确地位于先前指定的位置(在 E 驱动器等上)
在 SSMS 中打开一个查询窗口并连接到 Dev4 > 执行以下 SQL
使用大师;走
If(db_id(N'MYOBTraining') IS NULL) 创建数据库 MYOBTraining on (name = MYOBTraining, filename = 'D:\Database\Dev4\MYOBTraining.mdf' ) 登录 (name = MYOBTraining_log, filename = 'E:\Logs \Dev4\MYOBTraining_1.ldf' ) ; 走
运行以下 SQL:
从磁盘恢复数据库 MYOBTraining = 'E:\Backup\MYOBTraining_20201203_Full.bak' WITH FILE = 1 ,将 'MYOBTraining' 移至 'D:\Database\Dev4\MYOBTraining.mdf' ,移至 'log:\suptraining' Dev4\MYOBTraining_1.ldf' , NOUNLOAD, REPLACE, STATS = 1
显示以下错误:
消息 3634,级别 16,状态 1,第 17 行 尝试对“D:\数据库\MYOBTraining.mdf'。消息 3156,级别 16,状态 8,第 17 行文件“MYOBInterim”无法恢复到“D:\Database\MYOBTraining.mdf”。使用 WITH MOVE 标识文件的有效位置。消息 3634,级别 16,状态 1,第 17 行 尝试在“E:\日志\MYOBTraining_1.ldf'。消息 3156,级别 16,状态 8,第 17 行文件“MYOBInterim_log”无法恢复为“E: \Logs\MYOBTraining_1.ldf'。使用 WITH MOVE 标识文件的有效位置。消息 3119,级别 16,状态 1,第 17 行 规划 RESTORE 语句时发现问题。以前的消息提供了详细信息。消息 3013,级别 16,状态 1,第 17 行 RESTORE DATABASE 异常终止。
我的关键点:注意错误消息中“MYOBInterim”的存在 -
restore
) SQL 语句并将“MOVE MYOBTraining”替换为“MOVE MYOBInterim”(日志文件类似),则该restore
语句有效(并向我显示 Dev4 中名为 MYOBTraining 的数据库)。在答案中使用建议的附加信息
按照约翰的建议,在成功恢复数据库之前,我刚刚运行了这个:
RESTORE FILELISTONLY FROM DISK = 'E:\Backup\MYOBTraining_20201203_Full.bak'
Run Code Online (Sandbox Code Playgroud)
...实际上它将 LogicalName 值显示为 'MYOBInterim' 和 'MYOBInterim_Log' - 所以我的问题正是这样:为什么它在 SSMS 中显示为 'MYOBTraining'?
然而,PhysicalName 值与答案中的预期值不匹配。相反,它们是:
接下来,运行下面的第二个建议查询:
RESTORE HEADERONLY FROM DISK = 'E:\Backup\MYOBTraining_20201203_Full.bak'
Run Code Online (Sandbox Code Playgroud)
数据库名称显示 'MYOBTraining'
...所以我想这回答了我的问题:SSMS 显示数据库名称,但还原语句需要 LogicalName。
在这种情况下,我可以将我的问题修改为:为什么?DatabaseName 和 LogicalName 有什么区别?如果我什至在 SSMS 中都看不到 LogicalName 有什么用?
快速在线搜索显示数据库存在三个名称:数据库名称(在 SSMS 中)、逻辑名称(由 SQL Server 内部引用?)和物理名称(文件名),并向我展示了如何检查这三个名称,但没有并不能真正解释为什么 MS 这样做。
可能的答案
好的,让我炖足够长的时间,我想你已经为我指明了答案的方向:
restore
语句正在引用文件 - 在我的情况下,这些文件的逻辑名称由“MYOBInterim”组成听起来合理吗?
当您有备份文件时,如果您可以先运行以下命令,它总是有帮助的:
RESTORE FILELISTONLY FROM DISK = 'E:\_DBA\BackupFiles\MYOBTraining.bak'
Run Code Online (Sandbox Code Playgroud)
这将返回备份文件中包含的文件的信息。我猜您的.bak
文件的结果将与此类似:
Run Code Online (Sandbox Code Playgroud)+-----------------+----------------------------------+-----+ | LogicalName | PhysicalName | ... | +-----------------+----------------------------------+-----+ | MYOBInterim | D:\Database\Prod\MYOBInterim.mdf | ... | | MYOBInterim_log | E:\Logs\Prod\MYOBInterim_1.ldf | ... | +-----------------+----------------------------------+-----+
现在,如果您使用以下命令查看备份文件的 Header 信息:
RESTORE HEADERONLY FROM DISK = 'E:\_DBA\BackupFiles\MYOBTraining.bak'
Run Code Online (Sandbox Code Playgroud)
...你可能会有这样的事情:
Run Code Online (Sandbox Code Playgroud)+-----+--------------+-----+ | ... | DatabaseName | ... | +-----+--------------+-----+ | ... | MYOBInterim | ... | +-----+--------------+-----+
因此,您的数据库MYOBInterim
包含两个逻辑文件MYOBInterim
and MYOBInterim_log
,其中包含物理文件D:\Database\Prod\MYOBInterim.mdf
and E:\Logs\Prod\MYOBInterim_1.ldf
(或您实际拥有的任何文件)。
当您将数据库从一个位置恢复到另一个位置并且数据库名称和数据库文件的物理名称不同时,您必须将旧的逻辑名称重新映射到新的物理位置,同时提供新的数据库名称。
这就是您在脚本中所做的。
您观察到的错误消息是因为在您提供新的逻辑名称和新的物理位置时恢复最初失败,并且因为该*.bak
文件包含您正在恢复的数据库的名称,所以它显示该名称。
我通过将我的任何数据库恢复到新数据库并提供新的逻辑名称和新的物理位置来进行复制:
RESTORE DATABASE Q280721 FROM DISK = 'C:\SQL\BACKUP\StackExchange\FULL\StackExchange_FULL_20201128_223110.bak'
WITH FILE = 1, REPLACE,
MOVE 'Q280721' TO 'C:\SQL\SQL_DATA\Q280721.mdf',
MOVE 'Q280721_log' TO 'C:\SQL\SQL_LOGS\Q280721_log.ldf'
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)Msg 3156, Level 16, State 4, Line 1 File 'StackExchange' cannot be restored to 'C:\SQL\SQL_DATA\StackExchange.mdf'. Use WITH MOVE to identify a valid location for the file.
你的观察是错误的。让我们来看看你所说的:
我的关键点:注意错误消息中“MYOBInterim”的存在 -
确切的消息是(强调):
消息 3156,级别 16,状态 8,第 17 行文件“MYOBInterim”无法恢复到“D:\Database\MYOBTraining.mdf”。使用 WITH MOVE 标识文件的有效位置。
错误消息是由逻辑文件名而不是数据库引起的。因为您MOVE
在初始RESTORE DATABASE...
命令中提供了 a ,所以它试图将逻辑文件移动MYOBInterim
到新位置。MYOBInterim
您的备份文件中不存在逻辑文件名。因此出现错误消息。
这不是我为了进行备份而右键单击的 Dev 上 SSMS 中的数据库名称(在数据库文件夹中)
请参阅上面的先前解释。
这不在备份对话框中,该对话框显示选择备份的数据库为 MYOBTraining
请参阅上面的先前解释。
但是,如果我修改最后一个(恢复)SQL 语句并将“MOVE MYOBTraining”替换为“MOVE MYOBInterim”(日志文件类似),则恢复语句有效(并向我显示 Dev4 中名为 MYOBTraining 的数据库)。
这就是我在最初的回答中所解释的,并且我已经说过你做得对:
当您将数据库从一个位置恢复到另一个位置并且数据库名称和数据库文件的物理名称不同时,您必须将旧的逻辑名称重新映射到新的物理位置,同时提供新的数据库名称。
这就是您在脚本中所做的。
我希望这能回答你所有的问题。