Let's create a database and show how a data file becomes DEFUNCT.
USE master; GO --Create database CREATE DATABASE testdb ON PRIMARY ( NAME = testdb_data, FILENAME = 'D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb.mdf' ), FILEGROUP FG1 ( NAME = testdb_file1, FILENAME = 'D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_file1.ndf' ), ( NAME = testdb_file2, FILENAME = 'D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_file2.ndf' ) LOG ON ( NAME = testdb_log, FILENAME = 'D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_log.ldf' ); GO --Set database to simple recovery model ALTER DATABASE testdb SET RECOVERY SIMPLE; GO
The query above creates a database with the PRIMARY filegroup and FG1 secondary filegroup. The PRIMARY filegroup has one data file (mdf) and FG1 filegroup has two data files (ndf). The database is also set to simple recovery model.
Next, we creates some data.
USE testdb; GO --Create table on primary filegroup CREATE TABLE tbl1 ( col1 int IDENTITY (1,1) CONSTRAINT PK_tbl1_col1 PRIMARY KEY, col2 char(800) ); GO --Create index on FG1 filegroup CREATE INDEX IX_tbl1_col2 ON tbl1 ( col2 ) ON FG1; GO --Insert enough data so both secondary files in FG1 are filled with some data INSERT INTO tbl1 VALUES ('A'); GO 120
The query above created the table is created on PRIMARY filegroup, while its index is created on FG1 filegroup.
SELECT o.name tbl_name, i.name index_name, i.type_desc, f.name fg_name FROM sys.objects o JOIN sys.indexes i ON o.object_id = i.object_id JOIN sys.filegroups f ON i.data_space_id = f.data_space_id WHERE o.is_ms_shipped = 0; GO
The query above verified which filegroup the data is stored at. Now we try to make the data file in FG1 defunct. Let's first take a good backup for later use.
BACKUP DATABASE testdb TO DISK = 'testdb_good_backup.bak'; GO
Now, takes the first secondary file (testdb_file1) offline.
ALTER DATABASE testdb MODIFY FILE ( NAME = testdb_file1, OFFLINE ); GO
Warning. Please beware that the only way to bring the data file back online is restore from a backup.
SELECT f.name file_group, d.name file_name, d.state_desc file_state FROM sys.filegroups f JOIN sys.database_files d ON f.data_space_id = d.data_space_id; GO
The query above shows that the testdb_file1 data file is now offline. That also makes the FG1 filegroup offline as well. Any attempt to query data located on the offline filegroup returns error. The query below force the query to use the index located in the offline filegroup.
SELECT col2 FROM dbo.tbl1 WITH (INDEX(IX_tbl1_col2)); GO
Msg 315, Level 16, State 1, Line 56
Index "IX_tbl1_col2" on table "dbo.tbl1" (specified in the FROM clause)
is disabled or resides in a filegroup which is not online.
What about backing up the database at this moment?
BACKUP DATABASE testdb TO DISK = 'testdb_backup.bak'; GO
Msg 3007, Level 16, State 1, Line 115
The backup of the file or filegroup "testdb_file1" is not permitted because it is not online.
Container state: "Offline" (7). Restore status: 0. BACKUP can be performed by using the FILEGROUP
or FILE clauses to restrict the selection to include only online data.
Msg 3013, Level 16, State 1, Line 115
BACKUP DATABASE is terminating abnormally.
The only way to bring the data file back online is restore the database from the backup. You should always do that. However, if there is no backup and it has been determined that data in the offline filegroup is not needed, one way to allow backup is to make the filegroup defunct. That will remove the filegroup from the database but retain the metadata.
ALTER DATABASE testdb REMOVE FILEGROUP FG1; GO
Now if we check the status of the data files.
SELECT f.name file_group, d.name file_name, d.state_desc file_state FROM sys.filegroups f JOIN sys.database_files d ON f.data_space_id = d.data_space_id; GO
Even the testdb_file2 secondary file is online and is not empty, the FG1 filegroup removal completed successfully. Notice that both secondary files in FG1 filegroup are now in DEFUNCT state. Query against data located in this filegroup returns same error.
BACKUP DATABASE testdb TO DISK = 'testdb_defunct_backup.bak'; GO
Backup could be performed successful now with the understanding the data in the FG1 filegroup is no longer available.
As mentioned earlier, another way data files become DEFUNCT is during the piecemeal restore for database in simple recovery model. In this case, we restore the good testdb backup (testdb_good_backup.bak) taken earlier and restore only the primary filegroup to a new database (testdb_copy).
RESTORE DATABASE testdb_copy FILEGROUP = 'PRIMARY' FROM DISK = 'testdb_good_backup.bak' WITH MOVE 'testdb_data' TO 'D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_copy.mdf', MOVE 'testdb_log' TO 'D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_copy_log.ldf', PARTIAL; GO
Processed 184 pages for database 'testdb_copy', file 'testdb_data' on file 1.
Processed 2 pages for database 'testdb_copy', file 'testdb_log' on file 1.
Msg 3127, Level 16, State 1, Line 75
The file 'testdb_file1' of restored database 'testdb_copy' is being left in the defunct state because the database is using the simple recovery model and the file is marked for read-write access. Therefore, only read-only files can be recovered by piecemeal restore.
Msg 3127, Level 16, State 1, Line 75
The file 'testdb_file2' of restored database 'testdb_copy' is being left in the defunct state because the database is using the simple recovery model and the file is marked for read-write access. Therefore, only read-only files can be recovered by piecemeal restore.
RESTORE DATABASE ... FILE= successfully processed 186 pages in 0.049 seconds (29.595 MB/sec).
Running the below script shows the two secondary files in FG1 filegroup in DEFUNCT state.
USE testdb_copy; GO SELECT f.name file_group, d.name file_name, d.state_desc file_state FROM sys.filegroups f JOIN sys.database_files d ON f.data_space_id = d.data_space_id; GO
Note that the new database is piecemeal restore from a good complete backup. Full restore from the backup (testdb_defunct_backup.bak) taken after the defunct files creates database with the secondary files in DEFUNCT state, just like how it was.
Summary, data file becomes DEFUNCT when its filegroup is removed when one of its data file is not online. This action may be intentionally taken to allow tasks like backup to perform successfully when the offline file could not be found and there is no backup and more importantly, the data in the filegroup has been determined not needed or restored. Another situation is during piecemeal restore for database in simple recovery model that left the non-recovered file or filegroup in DEFUNCT state.
DEFUNCT data files affects SQL Server upgrade in some cases. More detail in next post.
DEFUNCT data files affects SQL Server upgrade in some cases. More detail in next post.
ReplyDeletePlease let';s know in more details
Well fone !
ReplyDeleteThanks for sharing.