Скопируйте базу данных SQL Server 2008 и переименуйте ее

У меня есть база данных SQL Server 2008, которую я хочу скопировать и создать на сервере новую базу данных (с другим именем). Я не заинтересован в сохранении данных, новая база данных может быть создана без данных для стартеров. Я хочу сделать следующее:

  • Создать новую базу данных, поддерживающую структуру старой базы данных
  • Задайте имя новой базы данных
  • Измените все типы данных varchar и char на nvarchar и nchar
  • Измените все текстовые типы данных на nvarchar (MAX)

В стороне у меня есть еще два вопроса, которые не входят в мою задачу, но хотели бы рассмотреть следующее:

  • Как обновить базу данных sql-сервера до SQL Server 2012
  • Есть ли какая-либо подготовительная работа, которую мне нужно выполнить в базе данных, чтобы я мог ее легко обновить?

Ответ 1

Это действительно комбинация нескольких вопросов.


ВОПРОСЫ 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';

Вам нужно будет заменить dbname и newname вашими фактическими именами баз данных, а также C:\some folder и C:\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-A, переименованную как DB-B:

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)

Рефакторинг - большая боль, особенно если некоторые из этих столбцов участвуют в ограничениях. Вы можете создать очень простой script таким образом, но вам понадобится нечто гораздо более промышленное, чтобы справиться со всеми этими переменными. Это предполагает, что все столбцы являются обнуляемыми и не участвуют в ограничениях.

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 script, и, если вы считаете, что это выглядит хорошо, раскомментируйте EXEC.

После завершения работы вы захотите перестроить все свои индексы.

Таким образом, сценарий базы данных, как предложил Тони (или с помощью инструмента, такого как Red Gate SQL Compare - или одна из его многочисленных альтернатив - против пустой базы данных), вероятно, будет намного проще, особенно если некоторые из этих столбцов участвуют в ограничениях - которые, возможно, необходимо удалить и повторно создать чтобы изменить типы.


ВОПРОСЫ 5 И 6

  • Как обновить базу данных sql-сервера до SQL-сервера 2012
  • Есть ли какая-либо подготовительная работа, которую мне нужно выполнить в базе данных, чтобы я мог ее легко обновить?

Вы не можете обновить только одну базу данных в экземпляре 2008 года. Вы либо обновляетесь, либо настраиваете новый экземпляр (как описано Тони), а затем перенастраиваете свою базу данных (желательно с помощью резервного копирования/восстановления), многие люди скажут вам отсоединить/присоединить, но это гораздо менее безопасно). Подготовительная работа, которую вы должны сделать, включает:

И после обновления вы захотите:

  • установите уровень совместимости на 110
  • обновить всю статистику

Ответ 2

Источник script, который копирует базу данных.

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)

Ответ 3

Хммм используйте sql-сервер для script из структуры базы данных, а затем выполните поиск и замените изменения. Установите 2012 и выполните script.

В 2008 году я ничего не знаю о том, что в 2012 году это не сработает. Если ваш db 2008 был настроен на совместимость с 2000 годом, тогда есть несколько проблем, но сценарий 2008 года должен обойти их прямо выкл.

Что касается обновления, у меня никогда не было много времени для обновления на месте, кроме всего прочего он разрушительный, поэтому любой смутно разумный сделало бы полную резервную копию сервера в любом случае. Не слушайте никого, кто говорит, что это вряд ли пойдет не так, по моему опыту, это означает, что вы, вероятно, будете неудачливым человеком, для которого он поступил неправильно, потому что вы не слишком сильно скрестили пальцы.

Лично я бы установил 2012 (желательно на другую машину) и просто восстановил резервную копию данных 2008 года. Затем установите уровни совместимости, исправьте пользователя/логины и т.д.

Если вы идете на место, вы не можете изменить битту. т.е. 32-битный с 2008 года по 64 бит в 2012 году. Для этого вам сначала нужно перейти на 2008 бит 64, что является еще одной причиной, чтобы предпочесть мое первое предложение.