Technical Article

Restore 2000 DB to 2005

,

This script was designed to restore a backup of a SQL Server 2000 database from disk file to SQL Server 2005.

Unfortunatelly the RESTORE will leave land mines for you to find the hard way, this script was updated to fix all that I have found:

Change CONCAT_NULL_YIELDS_NULL ON, PAGE_VERIFY CHECKSUM, Compatibility level to 90, remove the database role and user schema creation, and reassign the user's schema to dbo.

Like the ealier RestoreDB script posted here, this version will handle multiple DB files, move the files to new location, with new database name that is customized for the date of the backup, create any missing logins, and fix any user accounts that are orphaned by differing SIDs from the other server.

-- RestoreDB_8_9_Single script 9/13/2007
-- 
-- Restore the SQL 2000 (8) database backup to SQL 2005 (9)
--rename single database
--optional append _MoDay of backup to database name
--rename logical & physical files
--change paths for physical files
--set CONCAT_NULL_YIELDS_NULL ON
--change database options for 9
--fix orphaned User accounts
--create missing Login accounts
--fix duplicate spid issue
--replace Login (8) to Schema (9) conversion with dbo
--remove Login Schema
--
-- Search for %%% to find areas that need customization
--
USE master
GO

PRINT 'Restore Single database, Fixing Logins and User accounts at '+RTRIM(CONVERT(varchar(24),GETDATE(),121))
PRINT ''
GO

DECLARE @DB_Name varchar(128)
, @DB_BAK varchar(128)
, @UseMoDay bit
, @Password varchar(128)
, @DB_Path varchar(260)
, @Blob_Path varchar(260)
, @Img_Path varchar(260)
, @Idx_Path varchar(260)
, @Log_Path varchar(260)

SELECT @DB_Name = 'x_Config4'-- %%% database name to restore backup as  
, @DB_BAK = '\\DC1\SQL1\hd_RW3_Config4_Full' -- %%% path for the backup file
, @UseMoDay = 1 -- %%% Whether to _MoDay of backup to DB_Name
, @Password = 'password' -- %%% default password for new logins
-- %%% default paths for the new database files - FileGroup
--Search and replace these file group names
, @DB_Path = 'E:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\' -- 'PRIMARY'
, @Idx_Path = 'E:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\' -- 'INDICES'
, @Blob_Path = 'E:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\' -- 'TEXTIMAGE'
, @Img_Path = 'E:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\' -- 'IMGVAULT'
, @Log_Path = 'D:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\' -- no file group name for log

SET NOCOUNT ON
DECLARE @NewDB varchar(128)
, @LogicalName varchar(128)
, @NewLogicalName varchar(128)
, @PhysicalName varchar(260)
, @DatabaseName varchar(128)
, @Ext varchar(20)
, @FileType char(1)
, @MoDay varchar(4)
, @FileGroup varchar(128)
, @ExecStr nvarchar(4000)
, @LoginName varchar(128)
, @Role varchar(128)
, @Type char(1)
, @Schema varchar(128)
, @Cnt int
, @Err int

IF (SELECT OBJECT_ID('tempdb..#Media')) > 0 
EXEC ('DROP TABLE #Media')
CREATE TABLE #Media (
LogicalName nvarchar(128),
PhysicalName nvarchar(260), 
Type char(1),
FileGroupName nvarchar(128),
Size numeric(20,0),
MaxSize numeric(20,0),
FileID bigint,
CreateLSN numeric(25,0),
DropLSN numeric(25,0),
UniqueID  uniqueidentifier,
ReadOnlyLSN numeric(25,0),
 ReadWriteLSN numeric(25,0),
BackupSizeInBytes bigint,
SourceBlockSize int,
FileGroupID int,
LogGroupGUID uniqueidentifier,
DifferentialBaseLSN numeric(25,0),
DifferentialBaseGUID uniqueidentifier,
IsReadOnly bit,
IsPresent bit);

IF (SELECT OBJECT_ID('tempdb..#Header')) > 0 
EXEC ('DROP TABLE #Header')
CREATE TABLE #Header (
BackupName nvarchar(128),
BackupDescription nvarchar(255),
BackupType smallint,
ExpirationDate datetime,
Compressed tinyint,
Position smallint,
DeviceType tinyint,
UserName nvarchar(128),
ServerName nvarchar(128),
DatabaseName nvarchar(128),
DatabaseVersion int,
DatabaseCreationDate datetime, 
BackupSize numeric(20,0),
FirstLSN numeric(25,0),
LastLSN numeric(25,0),
CheckpointLSN numeric(25,0),
DatabaseBackupLSN numeric(25,0),
BackupStartDate datetime,
BackupFinishDate datetime,
SortOrder smallint,
CodePage smallint,
UnicodeLocaleId int,
UnicodeComparisonStyle int,
CompatibilityLevel tinyint,
SoftwareVendorId int,
SoftwareVersionMajor int,
SoftwareVersionMinor int,
SoftwareVersionBuild int,
MachineName nvarchar(128),
Flags int,
BindingID uniqueidentifier,
RecoveryForkID uniqueidentifier,
Collation nvarchar(128),
FamilyGUID uniqueidentifier,
HasBulkLoggedData bit,
IsSnapshot bit,
IsReadOnly bit,
IsSingleUser bit,
HasBackupChecksums bit,
IsDamaged bit,
BeginsLogChain bit,
HasIncompleteMetaData bit,
IsForceOffline bit,
IsCopyOnly bit,
FirstRecoveryForkID uniqueidentifier,
ForkPointLSN numeric(25,0),
RecoveryModel nvarchar(60),
DifferentialBaseLSN numeric(25,0),
DifferentialBaseGUID uniqueidentifier,
BackupTypeDescription nvarchar(60),
BackupSetGUID uniqueidentifier);

SET @ExecStr = 'RESTORE HEADERONLY FROM DISK = '''+@DB_BAK+''''
INSERT INTO #Header 
EXEC(@ExecStr)
SELECT @MoDay = REPLACE(CONVERT(varchar(5),BackupStartDate,1),'/','')
, @DatabaseName = DatabaseName  
FROM #Header
IF (SELECT OBJECT_ID('tempdb..#Header')) > 0 
EXEC ('DROP TABLE #Header')

SET @NewDB = @DB_Name+CASE WHEN @UseMoDay = CAST(1 as bit) THEN '_'+@MoDay ELSE '' END
PRINT 'Restoring '+@DatabaseName+' from '+@DB_BAK+' to '+@NewDB
PRINT ''

IF (SELECT DB_ID(@NewDB)) > 0 
BEGIN
PRINT 'Dropping existing database '+@NewDB
SET @ExecStr = 'DROP DATABASE ['+@NewDB+'] '
EXEC (@ExecStr)
PRINT ''
END

SET @ExecStr = 'RESTORE FILELISTONLY FROM DISK = '''+@DB_BAK+''''
INSERT INTO #Media 
EXEC(@ExecStr)

DECLARE MediaCursor CURSOR LOCAL FAST_FORWARD FOR 
SELECT LogicalName, PhysicalName, Type, FileGroupName 
FROM #Media
ORDER BY Type, LogicalName

OPEN MediaCursor
-- Get the 1st record
FETCH NEXT FROM MediaCursor INTO @LogicalName, @PhysicalName, @FileType, @FileGroup
SET @ExecStr = 'RESTORE DATABASE ['+@NewDB+'] FROM DISK = '''+@DB_BAK+''' WITH '
-- Process the recordset
WHILE @@fetch_status = 0
BEGIN
SET @Ext = RIGHT(@PhysicalName,CHARINDEX('_',REVERSE(@PhysicalName)))
IF @FileType = 'L'
SET @ExecStr = @ExecStr + 'MOVE '''+@LogicalName+''' TO '''+@Log_Path+@NewDB+@Ext+''', '
ELSE
BEGIN
IF @FileGroup = 'PRIMARY'
SET @ExecStr = @ExecStr + 'MOVE '''+@LogicalName+''' TO '''+@DB_Path+@NewDB+@Ext+''', '
IF @FileGroup = 'INDICES'
SET @ExecStr = @ExecStr + 'MOVE '''+@LogicalName+''' TO '''+@Idx_Path+@NewDB+@Ext+''', '
IF @FileGroup = 'TEXTIMAGE'
SET @ExecStr = @ExecStr + 'MOVE '''+@LogicalName+''' TO '''+@Blob_Path+@NewDB+@Ext+''', '
IF @FileGroup = 'IMGVAULT'
SET @ExecStr = @ExecStr + 'MOVE '''+@LogicalName+''' TO '''+@Img_Path+@NewDB+@Ext+''', '
END
-- get the next record
FETCH NEXT FROM MediaCursor INTO @LogicalName, @PhysicalName, @FileType, @FileGroup
END -- Media loop
-- Clean up
CLOSE MediaCursor
DEALLOCATE MediaCursor
IF (SELECT OBJECT_ID('tempdb..#Media')) > 0 
EXEC ('DROP TABLE #Media')
SET @ExecStr = LEFT(RTRIM(@ExecStr),LEN(RTRIM(@ExecStr))-1)
PRINT 'Restoring database '''+@DatabaseName+''' from backup at '+RTRIM(CONVERT(varchar(24),GETDATE(),121))
PRINT ''
EXEC(@ExecStr)
SET @Err = @@ERROR
IF @Err = 0
BEGIN
DECLARE LogiFileCursor CURSOR LOCAL FAST_FORWARD FOR
SELECT name, physical_name 
FROM sys.master_files 
WHERE database_id = DB_ID(@NewDB)
ORDER BY file_id

OPEN LogiFileCursor
-- Get the 1st record
FETCH NEXT FROM LogiFileCursor INTO @LogicalName, @PhysicalName
-- Process the recordset
IF @@FETCH_STATUS = 0
BEGIN
PRINT ''
PRINT 'Renaming logical database file names'
PRINT ''
END
WHILE @@FETCH_STATUS = 0
BEGIN
SET @NewLogicalName = RIGHT(@PhysicalName,CHARINDEX('\',REVERSE(@PhysicalName))-1)
SET @NewLogicalName = LEFT(@NewLogicalName,CHARINDEX('.',@NewLogicalName)-1)
IF @LogicalName <> @NewLogicalName
BEGIN
SET @ExecStr = 'ALTER DATABASE ['+@NewDB+'] MODIFY FILE (NAME = '''+@LogicalName
SET @ExecStr = @ExecStr+''', NEWNAME = '''+@NewLogicalName+''') '
EXEC(@ExecStr)
END
-- get the next record
FETCH NEXT FROM LogiFileCursor INTO @LogicalName, @PhysicalName
END -- Logical file loop
-- Clean up
CLOSE LogiFileCursor
DEALLOCATE LogiFileCursor

PRINT ''
PRINT 'Finished Restoring database '''+@DatabaseName+''' from backup at '+RTRIM(CONVERT(varchar(24),GETDATE(),121))
PRINT ''

PRINT 'Fix database options for SQL 2000 default'
PRINT CHAR(9)+'Change '+@NewDB+' CONCAT_NULL_YIELDS_NULL to ON'
SET @ExecStr = 'ALTER DATABASE ['+@NewDB+'] SET CONCAT_NULL_YIELDS_NULL ON ;'
EXEC(@ExecStr)
PRINT ''

PRINT 'Fix database options for SQL 2005 default'
PRINT CHAR(9)+'Change '+@NewDB+' Page Verify to CHECKSUM'
SET @ExecStr = 'ALTER DATABASE ['+@NewDB+'] SET PAGE_VERIFY CHECKSUM ; '
-- %%% Comment next line to leave PAGE_VERIFY TORN_PAGE_DETECTION
EXEC(@ExecStr)
PRINT CHAR(9)+'Change '+@NewDB+' Compatibility level to 90'
-- %%% Comment next line to leave Compatibility level at 80
EXEC sp_dbcmptlevel @NewDB, 90
PRINT ''

IF (SELECT OBJECT_ID('tempdb..#Users')) > 0 
EXEC ('DROP TABLE #Users')
CREATE TABLE #Users (LoginName varchar(128), RoleName varchar(128), Type char(1), SchemaName varchar(128));
SET @ExecStr = 'SELECT u.name, r.name, u.type, u.default_schema_name '
+ 'FROM '+@NewDB+'.sys.database_principals AS u '
+ 'LEFT JOIN '+@NewDB+'.sys.database_role_members AS m ON u.principal_id = m.member_principal_id '
+ 'LEFT JOIN '+@NewDB+'.sys.database_principals AS r ON m.role_principal_id = r.principal_id AND r.type =''R'' '
+ 'WHERE u.type <> ''R'' '
+ 'AND u.name NOT IN (''sa'',''dbo'',''guest'',''INFORMATION_SCHEMA'',''sys'') '
INSERT INTO #Users 
EXEC(@ExecStr)

-- Create the recordset for existing users
DECLARE UserCursor CURSOR LOCAL FAST_FORWARD FOR 
SELECT LoginName, RoleName, Type, SchemaName
FROM #Users
ORDER BY LoginName

OPEN UserCursor
-- Get the 1st record
FETCH NEXT FROM UserCursor INTO @LoginName, @Role, @Type, @Schema
-- Process the recordset
IF @@FETCH_STATUS = 0
BEGIN
PRINT 'Fixing Logins and User accounts at '+RTRIM(CONVERT(varchar(24),GETDATE(),121))
PRINT ''
END
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT char(9) + 'Login: ''' + @LoginName+ISNULL(''' Role: ''' + @Role,'')+ISNULL(''' Schema: '''+@Schema,'')+''''
-- See if the Login exists
IF NOT EXISTS (SELECT name FROM sys.server_principals WHERE type ='S' AND name = @LoginName)
BEGIN
-- Create the Login, using the default password
-- Requires ALTER ANY LOGIN
SET @ExecStr = 'CREATE LOGIN ['+@LoginName+'] WITH PASSWORD = '''+@Password+''', CHECK_POLICY = OFF '
EXEC(@ExecStr)
PRINT char(9) + char(9) + 'Created Login for ' + @LoginName
END
-- See if the User account is orphaned
SET @ExecStr = 'SELECT @Rows = COUNT(u.name) FROM '+@NewDB+'.sys.database_principals AS u LEFT JOIN sys.server_principals AS l ON u.sid = l.sid WHERE l.sid IS NULL AND u.name = '''+@LoginName+''' '
EXEC sp_executesql @ExecStr, N'@Rows int out', @Cnt out
IF @Cnt > 0
BEGIN
SET @ExecStr = 'EXEC '+@NewDB+'.dbo.sp_change_users_login ''Auto_Fix'', '''+@LoginName+''' '
EXEC(@ExecStr)
PRINT char(9) + char(9) + 'Fixed User account for ' + @LoginName
END
-- Test again to fix for 'duplicate SID'
SET @ExecStr = 'SELECT @Rows = COUNT(u.name) FROM '+@NewDB+'.sys.database_principals AS u LEFT JOIN sys.server_principals AS l ON u.sid = l.sid WHERE l.sid IS NULL AND u.name = '''+@LoginName+''' '
EXEC sp_executesql @ExecStr, N'@Rows int out', @Cnt out
IF @Cnt > 0
BEGIN
-- Requires ALTER ANY USER
SET @ExecStr = 'USE ['+@NewDB+']; DROP USER ['+@LoginName+']; '
EXEC(@ExecStr)
PRINT char(9) + char(9) + 'Deleted User account for ' + @LoginName
-- Requires ALTER ANY USER
SET @ExecStr = 'USE ['+@NewDB+']; CREATE USER ['+@LoginName+'] FOR LOGIN ['+@LoginName+']; '
EXEC(@ExecStr)
SET @ExecStr = 'EXEC '+@NewDB+'.dbo.sp_grantdbaccess @loginame = '''+@LoginName+''' ' 
EXEC(@ExecStr)
PRINT char(9) + char(9) + 'Granted '+@NewDB+' access to ' + @LoginName
END
IF ISNULL(@Schema,'dbo') <> 'dbo'
BEGIN
-- Change the default schema (for converted DB, will be LoginName instead of dbo)
-- Requires ALTER ANY USER
SET @ExecStr = 'USE ['+@NewDB+']; ALTER USER ['+@LoginName+'] WITH DEFAULT_SCHEMA = dbo; '
EXEC(@ExecStr)
-- Drop the SCHEMA if it is no longer related
SET @ExecStr = 'USE ['+@NewDB+']; IF EXISTS (SELECT * FROM sys.schemas AS s INNER JOIN sys.sql_logins AS l ON s.name = l.name LEFT JOIN sys.objects AS o ON s.schema_id = o.schema_id WHERE s.name NOT IN (''sa'',''dbo'',''guest'',''sys'',''INFORMATION_SCHEMA'') AND o.object_id IS NULL AND s.name = '''+@LoginName+''') '
+'DROP SCHEMA ['+@LoginName+']; '
EXEC(@ExecStr)
END
IF LEN(@Role) > 0
BEGIN
-- Drop the role schema if it exists
SET @ExecStr = 'USE ['+@NewDB+']; IF EXISTS (SELECT * FROM sys.schemas WHERE name = '''+@Role+''') '
+'DROP SCHEMA ['+@Role+']; '
EXEC(@ExecStr)
END
-- get the next record
FETCH NEXT FROM UserCursor INTO @LoginName, @Role, @Type, @Schema
END -- User account loop
-- Clean up
CLOSE UserCursor
DEALLOCATE UserCursor
IF (SELECT OBJECT_ID('tempdb..#Users')) > 0 
EXEC ('DROP TABLE #Users')
END -- @Err = 0
GO

SET NOCOUNT OFF
PRINT ''
PRINT 'Finished Restore Single database, Fixing Logins and User accounts at '+RTRIM(CONVERT(varchar(24),GETDATE(),121))
GO

Rate

4 (1)

You rated this post out of 5. Change rating

Share

Share

Rate

4 (1)

You rated this post out of 5. Change rating