2

我们目前有一个 CMS 的基础安装,在这个 CMS 中,它包含一个完整的用户、产品、内容等工作数据集。我们希望增加安装时间,因为现在我们必须进入 SQL Server 2012,创建一个新数据库,然后从现有的基本安装数据库恢复数据库。

我们每次安装最多可能需要 10 到 15 分钟。

我们还确保我们的基础数据库具有我们构建的站点的所有要求。

我们的问题是,我们想做以下事情。

  1. 有一个脚本创建一个全新的空数据库
  2. 将此数据库克隆到一个新的 .bak 文件中
  3. 获取这个 .bak 文件,然后将其复制到一个新的数据库中,分别具有自己的 MDF 和 LDF 文件。

数据库位于同一台服务器上,因此我们不需要将其迁移到任何其他机器或实例。

我们的代码如下

CREATE database my_test

BACKUP DATABASE test_db TO DISK = 'C:\my_test\my_test.bak' WITH INIT;
EXEC internal_lab_test.dbo.sp_helpfile;

RESTORE FILELISTONLY
  FROM DISK = 'C:\my_test\my_test.bak'

RESTORE DATABASE my_test
  FROM DISK = 'C:\my_test\my_test.bak'
  WITH MOVE 'my_test' TO 'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\test_db.mdf',
  MOVE 'my_test_log' TO 'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\test_db_log.ldf'

我们要确保一切都是新鲜和干净的,但仍然包含所有数据,但是每次运行此代码时,我们都会收到以下错误消息

我们还想确保原始数据库 mdf 和 ldf 文件完好无损,并且没有在新数据库中使用

Msg 3154, Level 16, State 4, Line 10
The backup set holds a backup of a database other than the existing 'my_test' database.
Msg 3013, Level 16, State 1, Line 10
RESTORE DATABASE is terminating abnormally.
4

2 回答 2

4

我知道这是旧的,但它是谷歌的第二个条目,所以为了完整性。

这是因为数据库已经存在。所以要么删除数据库,要么用替换添加。

my_test 和 my_test_log 名称也需要是 restore filelistonly 命令中的逻辑名称。

RESTORE DATABASE my_test
  FROM DISK = 'C:\my_test\my_test.bak'
  WITH Replace,
  MOVE 'my_test' TO 'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\test_db.mdf',
  MOVE 'my_test_log' TO 'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\test_db_log.ldf'
于 2013-10-08T23:13:56.297 回答
1

我编写的完整脚本对于常见的开发任务来说足够灵活(从“模板”克隆数据库,应用增量脚本,然后删除克隆的数据库)。注意:您应该在adv.cloneDatabase执行过程之前将您的数据库设置为“只读,单用户”,并在之后返回原始状态,您还需要执行 xp_cmdshell 的权限。

CREATE PROCEDURE adv.alterateFileNames(
        @mdfFileName nvarchar(256), 
        @ldfFileName nvarchar(256), 
        @newMdfFileName nvarchar(256) OUTPUT, 
        @newLdfFileName nvarchar(256) OUTPUT
)
AS
BEGIN
    DECLARE @path_data nvarchar(256)
    DECLARE @ext_data nvarchar(4)
    DECLARE @path_log nvarchar(256)
    DECLARE @ext_log nvarchar(4)

    -- respect file extensions
    if (RIGHT(@mdfFileName , 4)='.mdf')
    BEGIN
        SET @path_data = SUBSTRING(@mdfFileName,0,LEN(@mdfFileName)-3)
        SET @ext_data = '.mdf'
    END
    ELSE
    BEGIN
        SET @path_data = @mdfFileName
        SET @ext_data = ''
    END


    if (RIGHT(@ldfFileName , 4)='.ldf')
    BEGIN
        SET @path_log = SUBSTRING(@ldfFileName,0,LEN(@ldfFileName)-3)
        SET @ext_log = '.ldf'
    END
    ELSE
    BEGIN
        SET @path_log = @ldfFileName
        SET @ext_log = ''
    END

    -- respect suffix counters like dbname_2 (that means add value to them)
    DECLARE @iData int

    DECLARE @data_suffix_index int = len(@path_data) - charindex('_', reverse(@path_data))
    IF (@data_suffix_index < len(@path_data)-1 AND @data_suffix_index > 0 )
    BEGIN
        DECLARE @data_suffix nvarchar(128) = substring(@path_data, @data_suffix_index+2, len(@path_data)-@data_suffix_index-1 ) 
        IF @data_suffix NOT LIKE '%[^0-9]%'
        BEGIN
            SET @path_data = SUBSTRING(@path_data,0,@data_suffix_index+1)
            SET @iData = CAST(@data_suffix as int);
        END
    END
    IF (@iData is null)
    BEGIN
            SET @path_data = @path_data
            SET @iData = 0
    END 

    DECLARE @iLog int

    DECLARE @log_suffix_index int = len(@path_log) - charindex('_', reverse(@path_log))
    IF (@log_suffix_index < len(@path_log)-1 AND @log_suffix_index > 0 )
    BEGIN
        DECLARE @log_suffix nvarchar(128) = substring(@path_log, @log_suffix_index+2, len(@path_log) - @log_suffix_index-1 ) 
        IF @log_suffix NOT LIKE '%[^0-9]%'
        BEGIN
            SET @path_log = SUBSTRING(@path_log,0,@log_suffix_index+1)
            SET @iLog = CAST(@log_suffix as int);
        END
    END
    IF (@iLog is null)
    BEGIN
            SET @path_log = @path_log
            SET @iLog = 0
    END 

    WHILE 1=1
    BEGIN
        IF EXISTS(SELECT * FROM sys.master_files WHERE physical_name=@path_data+'_'+CAST(@iData AS varchar(6))+@ext_data)
            SET @iData=@iData+1
        ELSE
        BEGIN
            SET @path_data= @path_data+'_'+CAST(@iData AS varchar(6))+@ext_data
            BREAK
        END
    END

    WHILE 1=1
    BEGIN
        IF EXISTS(SELECT * FROM sys.master_files WHERE physical_name=@path_log+'_'+CAST(@iLog AS varchar(6))+@ext_log)
            SET @iLog=@iLog+1
        ELSE
        BEGIN
            SET @path_log= @path_log+'_'+CAST(@iLog AS varchar(6))+@ext_log
            BREAK
        END
    END
    SET @newMdfFileName = @path_data
    SET @newLdfFileName = @path_log
END
GO
CREATE PROCEDURE adv.cloneDatabase
( 
    @databaseName sysname,
    @newDatabaseName sysname
)
AS
BEGIN
    SET NOCOUNT ON

    IF NOT EXISTS ( SELECT * FROM sys.databases WHERE name = @databaseName)
        THROW 50000, 'Database doesn''t exist', 1;
    IF NOT EXISTS ( SELECT * FROM sys.databases WHERE name = @databaseName AND owner_sid<>0x01)
        THROW 50000, 'Clonning of system database is not supported', 1;
    IF NOT EXISTS ( SELECT * FROM sys.databases WHERE name = @databaseName AND is_read_only=1)
        THROW 50000, 'Clonning of not readonly database is not supported', 1;
    IF NOT EXISTS ( SELECT * FROM sys.databases WHERE name = @databaseName AND user_access=1 /*single user*/)
        THROW 50000, 'Clonning of nor single_user database is not supported', 1;

    -- collect file info
    DECLARE @Files TABLE 
    (
        [type] int, /*0,1,2,3,4*/
        type_desc nvarchar(60), /*ROWS,LOG,FILESTREAM,FULLTEXT*/
        name sysname,
        physical_name nvarchar(260)
    )

    INSERT INTO @Files ([type], type_desc, name, physical_name)
    SELECT [type], type_desc, f.name, physical_name
    FROM sys.master_files f INNER JOIN sys.databases d ON f.database_id=d.database_id
    WHERE d.name=@databaseName

    -- test files
    DECLARE @filesCount int
    SELECT @filesCount =  count(*) from @Files
    IF (@filesCount<>2)
        THROW 50000, 'The procedure doesn''t support complex file structures', 1;

    DECLARE @mdfFileName nvarchar(260), @ldfFileName nvarchar(260)
    SELECT @mdfFileName = physical_name FROM @Files WHERE type_desc='ROWS'
    SELECT @ldfFileName = physical_name FROM @Files WHERE type_desc='LOG'

    DECLARE @newMdfFileName nvarchar(260), @newLdfFileName nvarchar(260)
    exec adv.alterateFileNames @mdfFileName, @ldfFileName, @newMdfFileName=@newMdfFileName OUTPUT, @newLdfFileName=@newLdfFileName OUTPUT

    DECLARE @cmd1 nvarchar(4000)= 'copy /Y "@mdfFileName" "@newMdfFileName"'
    DECLARE @cmd2 nvarchar(4000)= 'copy "@ldfFileName" "@newLdfFileName"'
    SET @cmd1=replace(@cmd1,'@mdfFileName',@mdfFileName)
    SET @cmd1=replace(@cmd1,'@newMdfFileName',@newMdfFileName)
    SET @cmd2=replace(@cmd2,'@ldfFileName',@ldfFileName)
    SET @cmd2=replace(@cmd2,'@newLdfFileName',@newLdfFileName)

    DECLARE @OUTPUT TABLE (line text)
    DECLARE @result INT

    BEGIN TRY
        INSERT INTO @OUTPUT (line) VALUES ('> '+@cmd1)
        INSERT INTO @OUTPUT
        exec @result =xp_cmdshell @cmd1
        INSERT INTO @OUTPUT (line) VALUES ('> '+@cmd2)
        IF (@result <> 0)
           THROW 50000, 'Error copying mdf file',1

        INSERT INTO @OUTPUT
        exec @result =xp_cmdshell @cmd2
        IF (@result <> 0)
           THROW 50000, 'Error copying ldf file',1
    END TRY
    BEGIN CATCH
        SELECT * FROM @OUTPUT WHERE line is not null;
        THROW
    END CATCH
    DECLARE @createDatabaseSql nvarchar(max)
    SET @createDatabaseSql = '
    CREATE DATABASE '+@newDatabaseName+'
      ON (FILENAME = '''+@newMdfFileName+'''),
         (FILENAME = '''+@newLdfFileName+''')
   FOR ATTACH;'
   exec sp_executesql  @stmt = @createDatabaseSql

END
GO
于 2013-12-27T16:37:41.103 回答