當實例沒有做DDL Trigger和其它一些監控時,如何知道誰刪除了某個表?通過系統函數fn_dblog,fn_dump_dblog和默認跟蹤可以找到。
1. 創建測試環境:新建個表,插入一條數據,然后drop掉
CREATE DATABASE test
go
USE test
go
CREATE TABLE dbo.fnlog_test
(id INT IDENTITY ,val VARCHAR(10) DEFAULT 'x')
GO
CREATE CLUSTERED INDEX IX_ft_id
ON dbo.fnlog_test (ID)
GO
INSERT INTO dbo.fnlog_test
VALUES (DEFAULT )
GO
DROP TABLE fnlog_test
GO
2. 通過sys.fn_dblog,找出相關信息:
USE test
go
SELECT [Transaction ID],[Transaction Name],[Begin Time],[Server UID],SPID
FROM sys.fn_dblog(NULL,null)
WHERE [Transaction Name]='DROPOBJ'
go
3. 上一步中這里得到了事務ID,開始時間,Suid,SPID等,但是執行刪除的SPID可以已經logout或者被重用了。所以要找出“當時”的這個SPID。
先根據事務ID,找出被刪除的對象吧。查詢結果的“OBJECT: 9:245575913:0”,9是DB_ID,245575913是object_id,就是被刪除的表的object_id.
SELECT TOP(1) [Lock Information]
FROM sys.fn_dblog(NULL,NULL)
WHERE [Lock Information] LIKE '%SCH_M OBJECT%' AND [Transaction ID]='0000:000002e7'
go
4. 通常SQL Server實例安裝后會開啟一個默認跟蹤(Default Trace),這個跟蹤會記錄一引起級別較高的重要信息。先找到默認跟蹤
SELECT id,status,path FROM sys.traces
WHERE is_default=1
5. 根據前幾步中得到的trace path,事務ID,開始時間,SPID,object_id,通過默認跟蹤得到進一步的信息:
SELECT DatabaseID,NTUserName,HostName,ApplicationName,LoginName,
SPID,ObjectID,StartTime, EventClass,EventSubClass
FROM sys.fn_trace_gettable('D:\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Log\log_10.trc',1)
WHERE SPID=52 AND StartTime>'2013/07/15 11:32:44:133' AND ObjectID =245575913
GO
這一步中就得到了誰刪除了這個表的更具體信息了。需要說明一下的是EventClass=47,EventSubclass=(0,1),這記錄了跟蹤事件的操作。
SELECT te.trace_event_id,te.name,tsv.subclass_value,tsv.subclass_name FROM sys.trace_events te INNER JOIN sys.trace_subclass_values tsv ON te.trace_event_id=tsv.trace_event_id WHERE te.trace_event_id=47 AND tsv.subclass_value IN(0,1)
6. 如果是生產環境的,事務日志可能被截斷而被重用覆蓋了。這里就需要從日志備份中讀取日志信息來定位。需要用到fn_dump_dblog.
重新構建測試環境:
CREATE DATABASE test
go
USE test
go
CREATE TABLE dbo.fnlog_test
(id INT IDENTITY ,val VARCHAR(10) DEFAULT 'x')
GO
CREATE CLUSTERED INDEX IX_ft_id
ON dbo.fnlog_test (ID)
GO
INSERT INTO dbo.fnlog_test
VALUES (DEFAULT )
GO
USE master
go
BACKUP DATABASE test
TO DISK='D:\SQLSample\test.bak'
WITH init
go
USE test
go
DROP TABLE fnlog_test
GO
USE master
go
BACKUP LOG test
TO DISK='D:\SQLSample\test.bck'
WITH init
go
2. 和3. 的查詢要換成fn_dump_dblog,其它的步驟是一樣的。這里我另外做的測試,所以事務ID與前面不同了。
SELECT [Transaction ID],[Transaction Name],[Begin Time],[Server UID],SPID FROM fn_dump_dblog (NULL, NULL, N'DISK', 1, N'D:\SQLSample\test.bck', DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT) WHERE [Transaction Name] LIKE '%DROPOBJ%' SELECT TOP(1) [Lock Information] FROM fn_dump_dblog (NULL, NULL, N'DISK', 1, N'D:\SQLSample\test.bck', DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT) WHERE [Lock Information] LIKE '%SCH_M OBJECT%' AND [Transaction ID]='0000:000002b8'
總結:
1. 在SQL Server 2008 R2 SP2&SQL Server 2012 SP1測試通過
2. trace文件是rollover的,所以要找對path,同樣要從日志備份中查詢的話,也要找對日志備份文件的時間
3. fn_dblog和fn_dump_dblog是Undocumented Function.