【问题标题】:Is there a way to avoid MOVE errors on script restore?有没有办法避免脚本还原时出现 MOVE 错误?
【发布时间】:2019-06-14 17:41:58
【问题描述】:

我已经搜索了一段时间这个问题,如果没有,我无法找到解决方案。

每周我们都会从客户那里获得新的数据库。我开发了一个工具来恢复我们自己的数据库,以便让所有数据库与客户的数据库保持一致。 该工具适用于某些数据库,但在其他数据库上由于日志文件而出现一些错误。

我的数据库恢复脚本如下

USE [master] 
ALTER DATABASE[MyDataBase] SET SINGLE_USER WITH ROLLBACK IMMEDIATE 
RESTORE DATABASE[MyDataBase] 
FROM  DISK = N'MyDataBase.bak' 
WITH NOUNLOAD, 
REPLACE, STATS = 5 
ALTER DATABASE[MyDataBase] SET MULTI_USER

我知道如果我使用 MOVE 命令可以解决我的问题,问题是我无法事先知道文件,这意味着我无法真正编写任何自定义代码来恢复数据库。

再深入一点,我发现我可以使用以下命令打印数据库中的所有日志文件

SELECT 
  DB_NAME([database_id]) [database_name]
, [file_id]
, [type_desc] [file_type]
, [name] [logical_name]
, [physical_name]
FROM sys.[master_files]
WHERE [database_id] IN (DB_ID('MyDataBase'))
ORDER BY [type], DB_NAME([database_id]);

但是那里显示的文件与我得到错误的文件的名称完全不同。

要注意的另一件重要事情是,如果我还原数据库然后尝试通过 tsql 进行还原,我可以进行还原,但我有一个服务器代理作业重命名文件以保持干净,运行后我会无法再次恢复数据库,出现与手动恢复之前相同的错误。

我不知道我想要实现的目标是否可以实现,如果可以,如何实现。如果有人能给我一些灯,那就太棒了

【问题讨论】:

    标签: sql-server vb.net tsql


    【解决方案1】:

    RESTORE FILELISTONLY 会告诉你文件,然后你可以建立 RESTORE ... WITH MOVE :

    EG

    --backup database a to disk='c:\temp\a.bak'
    
    declare @fn nvarchar(255) = 'c:\temp\a.bak';
    declare @sql nvarchar(max) = concat('restore filelistonly from disk=''',@fn,'''');
    declare @targetFolder nvarchar(max) = 'c:\temp\customer_123';
    declare @dbname sysname = 'a_123';
    
    declare @t table
    (
    LogicalName nvarchar(128),--,   --Logical name of the file.
    PhysicalName    nvarchar(260),--    Physical or operating-system name of the file.
    Type    char(1),--  The type of file, one of:
    FileGroupName   nvarchar(128) NULL, --  Name of the filegroup that contains the file.
    Size    numeric(20,0),--    Current size in bytes.
    MaxSize numeric(20,0),--    Maximum allowed size in bytes.
    FileID  bigint,--   File identifier, unique within the database.
    CreateLSN   numeric(25,0),--    Log sequence number at which the file was created.
    DropLSN numeric(25,0) NULL, --  The log sequence number at which the file was dropped. If the file has not been dropped, this value is NULL.
    UniqueID    uniqueidentifier,-- Globally unique identifier of the file.
    ReadOnlyLSN numeric(25,0) NULL, --  Log sequence number at which the filegroup containing the file changed from read-write to read-only (the most recent change),--.
    ReadWriteLSN    numeric(25,0) NULL, --  Log sequence number at which the filegroup containing the file changed from read-only to read-write (the most recent change),--.
    BackupSizeInBytes   bigint, --  Size of the backup for this file in bytes.
    SourceBlockSize int, -- Block size of the physical device containing the file in bytes (not the backup device),--.
    FileGroupID int,-- ID of the filegroup.
    LogGroupGUID    uniqueidentifier NULL, --   NULL.
    DifferentialBaseLSN numeric(25,0) NULL, --  For differential backups, changes with log sequence numbers greater than or equal to DifferentialBaseLSN are included in the differential 
    DifferentialBaseGUID    uniqueidentifier NULL, --   For differential backups, the unique identifier of the differential base.
    IsReadOnly  bit,--  1 = The file is read-only.
    IsPresent   bit,--  1 = The file is present in the backup.
    TDEThumbprint   varbinary(32) NULL, --  Shows the thumbprint of the Database Encryption Key. The encryptor thumbprint is a SHA-1 hash of the certificate with which the key is encrypted. For information about database encryption, see Transparent Data Encryption (TDE),--.
    SnapshotURL nvarchar(360)-- NULL    The URL for the Azure snapshot of the database file contained in the FILE_SNAPSHOT backup. Returns NULL if no FILE_SNAPSHOT backup.
    );
    insert into @t
    exec (@sql);
    
    
    with q as 
    (
    select concat('restore database ',@dbname,' from disk=''',@fn,''' with ') l
    union all
    select concat('
    move ''',LogicalName,''' to ''', @targetFolder, '\', LogicalName, case [type] when 'D' then '.mdf' when 'L' then '.ldf' else null end, ''' , ')
    from @t 
    union all
    select 'RECOVERY, STATS = 10'
    )
    select @sql = STRING_AGG(l,'
    ')
    from q;
    
    
    print (@sql);
    exec (@sql );
    

    【讨论】:

    • 将尝试并提供反馈!这是一个工作问题,所以直到星期一我才准备好告诉它真的有效,但我看了一下 FILEISTONLY 并且 bak 文件有来自客户端的路径,所以我猜即使在映射所有文件之后它仍然给出一个由于 bak 文件而导致的错误。感谢代码伙伴:)
    • 这个想法是忽略备份中的路径和文件名,并将所有数据库文件移动到单个目录,文件名由文件的逻辑名称构成。
    • 是的!问题是我无法获取所有名称来构造名称。发生的事情是我们有同一个数据库的两种状态。 PRE 更新和 POS 更新。由于名称相同,我们必须重命名它们,这会导致一些问题。交叉手指让它使用 VB.NET 虽然 eheh
    • 工作就像一个魅力只是提醒......STRING_AGG 不能与所有 SMSS 2016 一起使用,所以我与 COALESCE 一起使用 concat 感谢脚本 David Browne :)
    猜你喜欢
    • 1970-01-01
    • 2013-04-11
    • 2019-09-07
    • 1970-01-01
    • 1970-01-01
    • 2011-05-16
    • 2019-05-29
    • 2011-09-13
    • 2020-11-20
    相关资源
    最近更新 更多