[SQL]關於掛載資料庫的三兩事

遇到一些朋友在掛載資料庫上遇到一些狀況 , 就順手整理一下相關指令讓自己複習一下

在早期的 SQL Server 的使用中,當我們將資料庫卸離之後,可以使用「sp_attach_db」或「sp_attach_single_file_db」的預存程序來做處理。這個主要是從早期 sybase 時代就遺留下來的方式,而隨著 SQL Server 的改版和相關語法的優化,都來都直接改用 CREATE DATABASE 的指令來做處理,雖然從早期就一值有在宣導要改用了,但為了相容舊版本的指令,到目前看起來都還是可以繼續使用。而如果您要是在 master 資料庫下面系統預存程序,查看那兩個預存程序的內容,會發現到其實它們也都是將 CREATE DATABASE 的指令給封裝起來,因此還是要建議大家最好改成官方所建議的 CREATE DATABASE 的指令來做處理,會是比較好的方式。

然而之所以寫這一篇,主要是剛好在前一個月的過程中,有兩個朋友都遇到掛載資料庫的問題,因此就順手整理一下相關的使用指令。第一個是遇到資料庫的交易紀錄檔( LDF )過大,因此他不知道從哪爬文找到的「偏方」,要他將資料庫給卸離之後,把交易紀錄檔給刪除,再重新利用類似以下的指令,將資料庫給重新掛起來。因為當您在掛載資料庫的時候,要是沒有提供交易紀錄檔,那麼不論透過 sp_attach_db 或 CREATE DATABASE 的指令,在交易紀錄檔內沒有任何需要 UNDO 或 REDO 的資料情況下,應該都可以順利把資料庫給掛上去,並且重新建立新的交易紀錄檔。那麼看起來似乎是個不錯的方式 ?! 但當您要這樣的使用的時候,那真的要非常非常的注意,因為那個是沒有任何需要 UNDO 或 REDO 的資料情況下,才有可能順利的完成。但假如情況並不是那樣完美的時候,那可能用有可能造成無法順利掛回資料庫的情況了。


為了說明相關的 CREATE DATABASE 的指令,我們先來建立一下測試環境,首先我們先透過以下的指令來建立資料庫和測試資料

-- 建立測試資料庫
CREATE DATABASE DEMO
GO


USE [DEMO]
GO

-- 建立測試資料表
CREATE TABLE T1( F1 INT PRIMARY KEY, F2 NVARCHAR(10) )
GO

-- 加入測試資料
INSERT INTO T1 VALUES ( 1,'A'),(2,'B'),(3,'C'),(4,'D')
GO

接下來我們為了製造一個有異常的資料庫,先用強迫關閉的方式

SHUTDOWN WITH NOWAIT

此時我們先將 DEMO 資料庫的 DEMO.mdf 檔案,將該檔案複製出來並且將新的檔案更改名稱為 DEMO1.mdf

接下來我們將 SQL Server 重新啟動起來,並且用以下指令繼續在範例資料表內加入一些資料

USE [DEMO]
GO

-- 加入測試資料
INSERT INTO T1 VALUES ( 10,'X'),(11,'Y'),(12,'Z')
GO

接下來我們用正常的方式卸離 DEMO 資料庫,接下來將 DEMO.mdf的檔案複製出來並且將新的檔案更改名稱為 DEMO2.mdf

USE [master]
GO
EXEC master.dbo.sp_detach_db @dbname = N'DEMO'
GO

當我們透過上述步驟,分別會有一個 DEMO1.mdf 資料檔和一個 DEMO2.mdf  資料檔,在接下來動作之前,也先把之前 DEMO 資料庫的相關檔案給刪除。

接著我們就可以來展示 CREATE DATABASE 的指令了,我們第一個展示的是搭配 ATTACH_REBUILD_LOG 的參數

USE [master]
GO
CREATE DATABASE [DEMO2] ON 
( FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL15.SQL2019\MSSQL\DATA\DEMO2.mdf' )
 FOR ATTACH_REBUILD_LOG
GO

因為我們在複製出來 DEMO2.MDF 的時候是屬於正常卸離的方式,因此當搭配上述指令的時候,SQL Server 會檢查 DEMO2.mdf內的資訊,去找是否有 DEMO_log.ldf 檔案,因為沒有該檔案,加上我們又搭配 ATTACH_REBUILD_LOG 的參數,因此這個時候 SQL Server 會搭配我們的指令,將資料檔案掛載起來變成 DEMO2 的資料庫,並且產生一個新的 DEMO2_log.ldf 的檔案

而此時我們透過 select 指令來看一下資料庫裏面的測試資料,看起來都有我們要的資料,算是成功地建立起交易紀錄檔並且恢復使用。

接著我們就回來類似前面的指令,來掛載剛剛沒有正常卸離的 DEMO1.mdf 的檔案。

USE [master]
GO

CREATE DATABASE [DEMO1] ON 
( FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL15.SQL2019\MSSQL\DATA\DEMO1.mdf' )
 FOR ATTACH_REBUILD_LOG
GO

但當我們透過上述指令去執行的時候,卻發現沒有辦法跟 DEMO2 資料庫一樣的順利,會出現錯誤代碼 1813 的錯誤

此時我們就要改變指令,將原本的 ATTACH_REBUILD_LOG 的參數改成 ATTACH_FORCE_REBUILD_LOG 的參數。當我們重新下指令之後,看起來就可以順利掛載 DEMO1 的資料庫,並且成功產生 DEMO1_log.ldf 的檔案。

看起來一切都是那麼美好,因此我們嘗試去撈資料表內的資料,此時會發現資料庫雖然可以順利掛載上去,但有可能會跟我類似的狀況,在資料庫內有可能會找不到 T1 的資料表。

只是看到這裡,有人會說那這樣幹嘛用 ATTACH_FORCE_REBUILD_LOG 指令呢 ? 通常這個都是用在不得已的狀況下,強迫去建立出交易紀錄檔,但如果原本的資料庫內有一些資料都還在交易紀錄檔內,還沒有寫回到資料檔內,因為沒有交易紀錄檔的情況下去強迫建立,勢必沒有辦法變回那些資料,因此這個只能在不得已的狀況下來做使用。

但是如果您按照我前面的步驟去做,也有可能因為 SQL Server 已經有自動 CheckPoint 了,所以也有可能在強迫重建交易紀錄檔後,還是會看到有範例的 T1 的資料表。