2010-05-14 77 views
2

我繼承了SQL Server 2008數據庫,調用應用程序可以通過存儲過程訪問該數據庫。是否可以確定是否從存儲過程發出DML命令?

數據庫中的每個表都有一個影子審計表,其中記錄了插入/更新/刪除操作。

對填充審計表進行性能測試表明,使用OUTPUT子句插入審計記錄的速度比使用觸發器快20%,所以這已在存儲過程中實現。但是,由於此設計無法跟蹤通過直接對錶發出的DML語句直接對錶進行的更改,因此還使用觸發器來使用值@@NESTLEVEL來確定是否運行觸發器(假設爲所有通過存儲過程運行的DML將具有@@NESTLEVEL> 1)。 即觸發代碼的機身看起來是這樣的:

IF @@NESTLEVEL = 1 -- implies call is direct sql so generate history from here 
    BEGIN 
... insert into audit table 

這種設計是有缺陷的,因爲它不會追蹤DML語句在動態SQL執行更新,或者@@NESTLEVEL以上1提出的任何其他方面。

任何人都可以提出一個完全可靠的方法,我們可以在觸發器中使用,只有在沒有觸發存儲過程時才能執行它們嗎?

或者這是(我懷疑)不可能?

回答

4

使用CONTEXT_INFO (Transact-SQL)。在此過程中設置的值,以提醒觸發器將不記錄任何東西:

--in the procedure doing the insert/update/delete 

DECLARE @CONTEXT_INFO varbinary(128) 
SET @CONTEXT_INFO =cast('SkipTrigger=Y'+REPLICATE(' ',128) as varbinary(128)) 
SET CONTEXT_INFO @CONTEXT_INFO 

--do insert/update/delete that will fire the trigger 

SET CONTEXT_INFO 0x0 

在觸發檢查CONTEXT_INFO,並確定是否需要做任何事情:

--here is the portion of the trigger to retrieve the value: 

IF CAST(CONTEXT_INFO() AS VARCHAR(128))='SkipTrigger=Y' 
BEGIN 
    --log your data here 
END 

的人只是在做一個流氓插入/更新/刪除它們不會設置CONTEXT_INFO並且觸發器將記錄更改。如果您認爲流氓代碼也會嘗試使用CONTEXT_INFO,則可以使用您輸入CONTEXT_INFO的值,比如表名或@@ SPID等。

+0

明智的做法是防守編程,並將您的代幣「添加」到CONTEXT_INFO中已經設置的任何代碼中?同樣,因爲它是基於連接的,不應該在程序結束時清除它們的標記嗎?如果一個proc調用一個proc,你應該只添加/清除它是否已經存在?可能會變得棘手,但仍然,這聽起來非常可靠。 – 2010-05-14 13:59:17

+1

@Philip Kelley,在上面的簡單示例中,我通過執行'SET CONTEXT_INFO 0x0'來清除它,您可以在更改它之前存儲它的值,然後將其設置回來。你也可以推/推動價值等等。可能性只是無盡的,這只是一個簡單的例子,而不是一個完整的機制。 – 2010-05-14 14:05:47

+0

謝謝 - 這是一個很好的解決方案。 – 2010-05-15 08:55:29

相關問題