Problem
You want to attach a SQL Server database that does not have the transaction log file and you get the following error when you try to attach the data file:
"The log cannot be rebuilt because there were open transactions/users when the database was shutdown, no checkpoint occurred to the database, or the database was read-only. This error could occur if the transaction log file was manually deleted or lost due to a hardware or environment failure."
In this tip I will show how you two methods that can successfully attach a database when you get this error.
Solution
Here I will cover the not so uncommon scenario where someone gives you a SQL Server database to attach to your instance, but only gives you the *.mdf file. Unfortunately, when you try to attach the database the SQL Server engine complains about the missing transaction log and aborts the attachment process.
Test Environment Setup
First, we will create our sample database and set the recovery model to Full by running the scripts below in SQL Server Management Studio.
USE [master]
GO
CREATE DATABASE [TestDB]
CONTAINMENT = NONE
ON PRIMARY
( NAME = N'TestDB_file1',
FILENAME = N'E:\MSSQL\TestDB_1.mdf',
SIZE = 128MB ,
MAXSIZE = UNLIMITED,
FILEGROWTH = 64MB)
LOG ON
( NAME = N'TestDB_log_file1',
FILENAME = N'E:\MSSQL\TestDB_1.ldf',
SIZE = 8MB,
MAXSIZE = 2048GB,
FILEGROWTH = 8MB)
GO
ALTER DATABASE TestDB SET RECOVERY FULL
GO
The next script will create our sample table.
USE TestDB
GO
SELECT *
INTO TestTable
FROM sys.objects
Now we are going to add some sample data. We want the insert statement to take enough time to let us force the shutdown of the test instance while it is still running. This will let the database be in an inconsistent state needing to perform recovery at the next database startup.
USE TestDB;
GO
INSERT INTO dbo.TestTable
SELECT a.*
FROM TestTable a
CROSS JOIN sys.objects b
CROSS JOIN sys.objects c
CROSS JOIN sys.objects d
In another window in SQL Server Management Studio execute the following statement to force the instance shutdown.
SHUTDOWN WITH NOWAIT
After stopping the instance, delete the log file then start up the SQL Server service. If you refresh the Databases view in SQL Server Management Studio you will see that our test database is inaccessible because it is marked as Recovery Pending, as shown on the next image.
At this point we have an orphaned and inconsistent database file.
First, let's clean the system catalog by dropping the database. We must set the database offline to copy or rename the data file that will be the subject for our tests.
USE master
GO
ALTER DATABASE TestDB SET OFFLINE
GO
Then we must clean the system catalog metadata by dropping the database.
USE master
GO
DROP DATABASE TestDB
GO
Trying to Attach the Damaged SQL Server Database
When you are asked to attach a database with one data file and no log, the first thing that may come to mind is the old and deprecated sp_attach_single_file_db.
USE master
GO
EXEC sys.sp_attach_single_file_db @dbname = 'TestDB',
@physname = N'E:\MSSQL\TestDBCopy.mdf'
GO
But after you execute the previous script you will see that it fails with this error message:
"The log cannot be rebuilt because there were open transactions/users when the database was shutdown, no checkpoint occurred to the database, or the database was read-only. This error could occur if the transaction log file was manually deleted or lost due to a hardware or environment failure."
See the image below as a point of reference.
Since sp_attach_single_file_db is deprecated and has been replaced with CREATE DATABASE..FOR ATTACH, let's try this to see if we have more luck.
USE [master]
GO
CREATE DATABASE [TestDB] ON
( FILENAME = N'E:\MSSQL\TestDBCopy.mdf' )
FOR ATTACH_REBUILD_LOG
GO
We face the same error message telling us that the log of the database cannot be rebuilt.
At this point we tried everything, but there are two methods we can use to recover the database, and I will cover both. The first method requires the usage of an undocumented command in the CREATE DATABASE statement. As you may know, the use of undocumented statements is not supported by Microsoft, so if something goes wrong you can't contact them. The other method is trickier, but does not use undocumented features.
The Undocumented ATTACH_FORCE_REBUILD_LOG Command
By now we know that if we use the CREATE DATABASE..FOR ATTACH statement to recover the database it will fail because we do not have the transaction log file. Also, since there were open transactions in the database, we cannot use the ATTACH_ REBUILD_LOG command. There is an undocumented ATTACH_FORCE_REBUILD_LOG Command that as its name suggests, forces the CREATE DATABASE..FOR ATTACH statement to recover the database even when there were orphaned opened transactions. Basically, it creates a new log file.
USE master;
CREATE DATABASE [TestDB]
ON ( FILENAME = N'E:\MSSQL\TestDB_1.mdf' )
FOR ATTACH_FORCE_REBUILD_LOG;
GO
In the next image we can see the execution of the previous command.
Attaching the Damaged SQL Server Database Without Using Undocumented Features
The first step is to create a new database.
USE [master]
GO
CREATE DATABASE [TestDB_Repair]
CONTAINMENT = NONE
ON PRIMARY
( NAME = N'TestDB_Repair_file1',
FILENAME = N'E:\MSSQL\TestDB_Repair_1.mdf',
SIZE = 8MB ,
MAXSIZE = UNLIMITED,
FILEGROWTH = 64MB)
LOG ON
( NAME = N'TestDB_Repair_log_file1',
FILENAME = N'E:\MSSQL\TestDB_Repair_1.ldf',
SIZE = 8MB,
MAXSIZE = 2048GB,
FILEGROWTH = 32MB)
GO
Now we set the database offline.
USE master
GO
ALTER DATABASE [TestDB_Repair] SET OFFLINE WITH ROLLBACK IMMEDIATE
GO
At this point we can change the file location of our new database to point to our orphaned mdf file and set the location of the log file to a non-existent file.
USE master
GO
ALTER DATABASE [TestDB_Repair] MODIFY FILE(NAME='TestDB_Repair_file1', FILENAME= 'E:\MSSQL\TestDBCopy.mdf')
ALTER DATABASE [TestDB_Repair] MODIFY FILE(NAME='TestDB_Repair_log_file1',
FILENAME= 'E:\MSSQL\TestDBCopy.ldf')
GO
Let's bring the database back online.
USE master
GO
ALTER DATABASE [TestDB_Repair] SET ONLINE
GO
We do not have to be SQL Server gurus to know that the previous script will fail. But if you take a look at the error message of the next screen capture you will see that when SQL Server does not find the transaction log file (remember that we changed the system catalog to point to a file that does not exist) it tries to rebuild it. And of course, its attempt to rebuild the log fails with the same error message we had while trying to attach our orphaned *.mdf file, only in this case the *.mdf was successfully attached leaving us one step closer to our objective.
Rebuilding the SQL Server Transaction Log
Now you will see that it is not very complicated to rebuild the SQL Server transaction log, but you must accept the fact that you will lose data. In fact, you should use this method to recover a damaged database if restoring the database from a backup is not possible. The reason behind this is that you can lose data other than the last user activity. For example, if a transaction was updating an index and the update operation performed a page split, you may lose the previously committed transactions that were no longer in the transaction log because page splits are a logged operation.
The next script includes several commands that I put together to bring our sample database back online. Further on I will explain the commands and why I decided to execute all of them in a single script, but first let's take a look.
USE master
GO
DBCC TRACEON(3604)
GO
ALTER DATABASE TestDB_Repair SET EMERGENCY
GO
ALTER DATABASE TestDB_Repair SET SINGLE_USER
GO
DBCC CHECKDB('TestDB_Repair', REPAIR_ALLOW_DATA_LOSS) WITH ALL_ERRORMSGS
GO
ALTER DATABASE TestDB_Repair SET MULTI_USER
GO
The first step in the previous script is to send all output from the DBCC commands to the query results instead of to the error log. The next two steps set the database to emergency mode and single user mode respectively, so we can execute DBCC CHECKDB with the REPAIR_ALLOW_DATA_LOSS option. Finally, the last command is to bring the database back to multi user mode.
In the next image you can see a screen capture of the execution of the previous script. I marked in red one of the output messages which states that the error log has been rebuilt.
'운영 > 백업과 복원' 카테고리의 다른 글
손상된 데이터베이스 복구 (0) | 2020.12.05 |
---|---|
RestoreVerify (0) | 2014.05.08 |
LiteSpeed 백업 옵션 (0) | 2014.01.16 |
백업 진행율 확인 (0) | 2014.01.15 |
데이터베이스 백업 기록 조회 (0) | 2013.12.11 |