【问题标题】:copy a SQL Server 2008 database and rename it复制 SQL Server 2008 数据库并重命名
【发布时间】:2012-09-11 05:53:54
【问题描述】:

我有一个 SQL Server 2008 数据库,我想复制它并在服务器上创建一个新数据库(使用不同的名称)。我不关心维护数据,可以在没有数据的情况下创建新数据库。我要做的是:

  • 创建新数据库维护旧数据库的结构
  • 设置新数据库的名称
  • 将所有 varchar 和 char 数据类型更改为 nvarchar 和 nchar
  • 将所有文本数据类型更改为 nvarchar(MAX)

顺便说一句,我还有 2 个问题不属于我的任务,但想考虑以下问题:

  • 如何将 sql server 数据库升级到 sql server 2012
  • 是否需要对数据库进行任何准备工作以确保可以轻松升级?

【问题讨论】:

    标签: sql database sql-server-2008 sql-server-2012


    【解决方案1】:

    script that copies a database 的来源。

    USE master;
    
    DECLARE
        @SourceDatabaseName AS SYSNAME = '<SourceDB>', 
        @TargetDatabaseName AS SYSNAME = '<TargetDB>'
    
    
    
    -- ============================================
    -- Define path where backup will be saved
    -- ============================================
    IF NOT EXISTS (SELECT 1 FROM sys.databases WHERE name = @SourceDatabaseName)
        RAISERROR ('Variable @SourceDatabaseName is not set correctly !', 20, 1) WITH LOG       
    
    DECLARE @SourceBackupFilePath varchar(2000)
    SELECT @SourceBackupFilePath = BMF.physical_device_name
    FROM
        msdb.dbo.backupset B
        JOIN msdb.dbo.backupmediafamily BMF ON B.media_set_id = BMF.media_set_id
    WHERE B.database_name = @SourceDatabaseName
    ORDER BY B.backup_finish_date DESC
    
    SET @SourceBackupFilePath = REPLACE(@SourceBackupFilePath, '.bak', '_clone.bak')
    
    IF @SourceBackupFilePath IS NULL
            RAISERROR ('Could not determine file path for backup file!', 16, 1) WITH LOG
    
    
    
    -- ============================================
    -- Backup source database
    -- ============================================
    DECLARE @Sql NVARCHAR(MAX) 
    SET @Sql = 'BACKUP DATABASE @SourceDatabaseName TO DISK = ''@SourceBackupFilePath'''
    SET @Sql = REPLACE(@Sql, '@SourceDatabaseName', @SourceDatabaseName)
    SET @Sql = REPLACE(@Sql, '@SourceBackupFilePath', @SourceBackupFilePath)
    SELECT 'Performing backup...', @Sql as ExecutedSql
    EXEC (@Sql)
    
    
    
    -- ============================================
    -- Automatically compose database files (.mdf and .ldf) paths
    -- ============================================
    DECLARE
              @LogicalDataFileName as NVARCHAR(MAX)
            , @LogicalLogFileName as NVARCHAR(MAX)
            , @TargetDataFilePath as NVARCHAR(MAX)
            , @TargetLogFilePath as NVARCHAR(MAX)
    
    SELECT
            @LogicalDataFileName = name,
            @TargetDataFilePath = SUBSTRING(physical_name,1,LEN(physical_name)-CHARINDEX('\',REVERSE(physical_name))) + '\' + @TargetDatabaseName + '.mdf'
    FROM sys.master_files
    WHERE
            database_id = DB_ID(@SourceDatabaseName)        
            AND type = 0            -- datafile file
    
    SELECT
            @LogicalLogFileName = name,
            @TargetLogFilePath = SUBSTRING(physical_name,1,LEN(physical_name)-CHARINDEX('\',REVERSE(physical_name))) + '\' + @TargetDatabaseName + '.ldf'
    FROM sys.master_files
    WHERE
            database_id = DB_ID(@SourceDatabaseName)        
            AND type = 1            -- log file     
    
    SELECT  
        @LogicalDataFileName as LogicalDataFileName,
        @LogicalLogFileName as LogicalLogFileName,
        @TargetDataFilePath as TargetDataFilePath,
        @TargetLogFilePath as TargetLogFilePath                
    
    IF @TargetDataFilePath IS NULL OR @TargetLogFilePath IS NULL
        RAISERROR ('Could not determine target paths!', 16, 1) WITH LOG
    
    
    
    -- ============================================
    -- Restore target database
    -- ============================================
    IF EXISTS (SELECT 1 FROM sys.databases WHERE name = @TargetDatabaseName)
        RAISERROR ('A database with the same name already exists!', 20, 1) WITH LOG        
    
    SET @Sql = 'RESTORE DATABASE @TargetDatabaseName
    FROM DISK = ''@SourceBackupFilePath'' 
    WITH MOVE ''@LogicalDataFileName'' TO ''@TargetDataFilePath'',
    MOVE ''@LogicalLogFileName'' TO ''@TargetLogFilePath''' 
    SET @Sql = REPLACE(@Sql, '@TargetDatabaseName', @TargetDatabaseName)
    SET @Sql = REPLACE(@Sql, '@SourceBackupFilePath', @SourceBackupFilePath)
    SET @Sql = REPLACE(@Sql, '@LogicalDataFileName', @LogicalDataFileName)
    SET @Sql = REPLACE(@Sql, '@TargetDataFilePath', @TargetDataFilePath)
    SET @Sql = REPLACE(@Sql, '@LogicalLogFileName', @LogicalLogFileName)
    SET @Sql = REPLACE(@Sql, '@TargetLogFilePath', @TargetLogFilePath)
    SELECT 'Restoring...', @Sql as ExecutedSql
    EXEC (@Sql)
    

    【讨论】:

      【解决方案2】:

      这确实是多个问题的组合。


      问题 1 和 2

      • 创建新数据库维护旧数据库的结构
      • 设置新数据库的名称

      最简单的备份命令是:

      BACKUP DATABASE dbname TO DISK = 'C:\some folder\dbname.bak' WITH INIT;
      -- or WITH INIT, COMPRESSION if you are on Enterprise or Developer
      

      现在要将其还原为不同的数据库,您需要知道文件名,因为它会尝试将相同的文件放在相同的位置。因此,如果您运行以下命令:

      EXEC dbname.dbo.sp_helpfile;
      

      您应该会看到包含数据和日志文件的名称和路径的输出。构建还原时,您需要使用这些路径,但将路径替换为新数据库的名称,例如:

      RESTORE DATABASE newname FROM DISK = 'C:\some folder\dbname.bak'
        WITH MOVE 'dbname' TO 'C:\path_from_sp_helpfile_output\newname_data.mdf',
        MOVE 'dbname_log' TO 'C:\path_from_sp_helpfile_output\newname_log.ldf';
      

      您必须将dbnamenewname 替换为您的实际数据库名称,并将C:\some folderC:\path_from_sp_helpfile_output\ 替换为您的实际路径。除非我知道这些是什么,否则我无法更具体地回答。


      这是一个完整的复制:

      CREATE DATABASE [DB-A];
      GO
      
      EXEC [DB-A].dbo.sp_helpfile;
      

      部分结果:

      name     fileid filename
      -------- ------ ---------------------------------
      DB-A     1      C:\Program Files\...\DB-A.mdf
      DB-A_log 2      C:\Program Files\...\DB-A_log.ldf
      

      现在我运行备份:

      BACKUP DATABASE [DB-A] TO DISK = 'C:\dev\DB-A.bak' WITH INIT;
      

      当然,如果克隆目标(在本例中为 DB-B)已经存在,您需要删除它:

      USE [master];
      GO
      IF DB_ID('DB-B') IS NOT NULL
      BEGIN
        ALTER DATABASE [DB-B] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
        DROP DATABASE [DB-B];
      END
      GO
      

      现在此恢复将成功运行,为您提供重命名为 DB-B 的 DB-A 副本:

      RESTORE DATABASE [DB-B] FROM DISK = 'C:\dev\DB-A.bak'
        WITH MOVE 'DB-A'     TO 'C:\Program Files\...\DB-B.mdf',
             MOVE 'DB-A_log' TO 'C:\Program Files\...\DB-B_log.ldf';
      

      问题 3 和 4

      • 将所有 varchar 和 char 数据类型更改为 nvarchar 和 nchar
      • 将所有文本数据类型更改为 nvarchar(MAX)

      重构是一个主要的痛苦,尤其是当其中一些列参与约束时。您可以通过这种方式构建一个非常基本的脚本,但您将需要更强大的工业实力来处理所有这些变量。这假定所有列都可以为空并且不参与约束。

      DECLARE @sql NVARCHAR(MAX) = N'';
      
      SELECT @sql += '
        ALTER TABLE ' + 
        QUOTENAME(OBJECT_SCHEMA_NAME(c.[object_id])) 
        + '.' + QUOTENAME(OBJECT_NAME(c.[object_id]))
        + ' ALTER COLUMN ' + QUOTENAME(c.name) + ' '
        + CASE t.name WHEN N'text' 
           THEN N'nvarchar(max)' 
           ELSE N'n' + t.name + '(' + RTRIM(c.max_length) + ')'
        END + ';'
      FROM sys.columns AS c
      INNER JOIN sys.types AS t
      ON c.user_type_id = t.user_type_id
      WHERE c.system_type_id IN (35, 167, 175)
      AND OBJECTPROPERTY(c.[object_id], 'IsMsShipped') = 0;
      
      PRINT @sql;
      -- EXEC sp_executesql @sql;
      

      您可以使用 PRINT 输出来验证脚本的前 8K,当您认为它看起来不错时,取消注释 EXEC

      完成后,您需要重建所有索引。

      也就是说,按照 Tony 的建议编写数据库脚本(或使用 Red Gate's SQL Compareone of its many alternatives 之类的工具 - 针对空数据库)可能会容易得多,特别是如果其中一些列参与约束 - 这可能需要删除并重新创建才能更改类型。


      问题 5 和 6

      • 如何将 sql server 数据库升级到 sql server 2012
      • 是否需要对数据库进行任何准备工作以确保可以轻松升级?

      您不能只升级 2008 实例上的单个数据库。您要么就地升级,要么设置一个新实例(如托尼所述),然后迁移您的数据库(最好使用备份/恢复 - 许多人会告诉您分离/附加,但这远不安全)。你应该做的准备工作包括:

      升级后你会想要:

      • 将兼容级别设置为 110
      • 更新所有统计数据

      【讨论】:

      • 一句话,美女!谢谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-16
      • 2013-04-24
      • 2014-12-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多