系統中的程序(Applicaion、SQL Agent Job、Stored Procedure、...)一多,尤其大部分中小企業的資料庫增刪改並沒有得到良好的管理,重複的操作一直在做,資料若是正確的那大家相安無事,如果資料發生錯誤的時候,開發人員就雞飛狗跳了,眾多程序重複增刪改相同的資料,讓追查錯誤來源變成一件苦差事,萬一錯誤的資料又跟錢有關,那更是壓力山大,還好科技始終來自於人性,利用 SQL Server 的 Trigger 加上 DMV(Dynamic Management Views)可以幫助我們找到線索。
Trigger(觸發程序)
建立 Trigger 的方式很簡單,只要在它上面按右鍵選擇「新增觸發程序」,SSMS 就會幫我們把產生 Trigger 的語法開好。

接下來我們輸入「Trigger 名稱」、「目標資料表名稱」、「Trigger 的事件」,這樣 Trigger 就會在 TriggerTest 這個資料表被 INSERT、UPDATE、DELETE 之後被觸發。

利用 DMV 來撒網
DMV 可以幫助我們取得當前的伺服器狀態,我們利用 sys.dm_exec_connections、sys.dm_exec_sessions、sys.dm_exec_requests 這三個 View,以及 sys.dm_exec_input_buffer() 這個 Function 並帶入 @@SPID 參數來幫我們找到嫌疑犯的機器名稱、客戶端名稱、來源位址、登入的 SQL 帳號、執行增刪改的 SQL 語句、...等線索。
SELECT
s.[host_name]
,s.program_name
,c.client_net_address
,c.client_tcp_port
,c.local_net_address
,c.local_tcp_port
,d.[Name] AS local_database_name
,N'TableName' AS local_table_name
,s.login_name
,(SELECT
ib.event_info
FROM sys.dm_exec_input_buffer(r.session_id, r.request_id) ib)
AS sql_text
FROM sys.dm_exec_sessions s
INNER JOIN sys.dm_exec_connections c
ON s.session_id = c.session_id
INNER JOIN sys.dm_exec_requests r
ON s.session_id = r.session_id
INNER JOIN sys.databases d
ON r.database_id = d.database_id
WHERE s.session_id = @@SPID;
鎖定被害人
由於 Trigger 是針對資料表的增刪改做觸發,所以如果是特定的資料列,我們必須撰寫篩選的邏輯來鎖定被害人,因此我鎖定的是 TriggerTest 資料表內 Id = 1 的 Name 欄位,當它被 UPDATE 時寫一筆記錄。

-- Suspect Table Create Script
CREATE TABLE [dbo].[Suspect] (
[id] [INT] IDENTITY (1, 1) NOT NULL
,[host_name] [NVARCHAR](128) NULL
,[program_name] [NVARCHAR](128) NULL
,[client_net_address] [NVARCHAR](48) NULL
,[client_tcp_port] [INT] NULL
,[local_net_address] [NVARCHAR](48) NULL
,[local_tcp_port] [INT] NULL
,[local_database_name] [NVARCHAR](128) NULL
,[local_table_name] [NVARCHAR](128) NULL
,[login_name] [NVARCHAR](128) NULL
,[sql_text] [NTEXT] NULL
,[created_time] [datetime] NULL
,CONSTRAINT [PK_Suspect] PRIMARY KEY CLUSTERED
(
[id] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Suspect] ADD CONSTRAINT [DF_Suspect_created_time] DEFAULT (GETDATE()) FOR [created_time]
GO
之後我就可以從搜集到的訊息記錄中,去縮小搜索範圍,相當有幫助。

