Sql-Server
事務完成時觸發的觸發器/事件
我有一個從大約 15 個不同的表中提取的查詢。我希望將其具體化為一個將其儲存在 xml/json 中的表。(以提高性能。)
我遇到的問題是這些表是由幾個程序更新的。如果可能的話,我正在尋找一種將其保存在 SQL Server 中的方法。
理想情況下,如果 SQL Server 有一個在事務送出之前觸發的觸發器,我會喜歡它,這樣我就可以查看受影響的表和記錄,並知道我是否需要更新“結果”表。
SQL Server 中有類似的東西嗎?
注意:我考慮過使用
INSTEAD OF
觸發器,但我無法知道事務中表的順序,所以如果事務更新了所有 15 個表,那麼我將為同一行更新“結果”表 15 次.
伙計,我非常抱歉。
我正在“回答”說沒有像您設計的那樣簡單的解決方案。
你向我們提出了一個非常有趣的挑戰。
在過去的四個小時裡,我一直在做研究和練習,以確保沒有辦法單獨收集依賴於 SQL Server 引擎的已送出事務的後果。
如果您可以向我們展示解決方案架構的宏偉視圖,我們將設計一個最省力的解決方案。
例如,在場景中:
- 一個或兩個 ADO.Net 應用程序
- 一個或兩個 EntityFramework 應用程序
- 一個或兩個 SSIS 工作
每個可能修改這些表上的數據,我會:
- 為每個表創建一個觸發器只是為了標記它已被回火
- 在每個呼叫將處理標記數據的 SP 的應用程序上創建一個攔截
而且,對於攔截點,我會:
- 在 ADO.Net 應用程序上 - 取代/覆蓋 AdoDbConnection 並依靠 VS 快速安全地重構程式碼
- 在 EF 應用程序上 - 覆蓋 DbContext 中的 SaveChanges
- 關於 SSIS 工作 - 在每個工作結束時包括一個電話
下面我將介紹:
- 將註冊要處理的事件的表的架構
- 將為目標表生成觸發器的腳本
- 應該是處理儲存過程的片段,由上面列出的擴展點呼叫
要處理的事件的系統資料庫
要註冊要後處理的記錄,我會設計一個表,例如:
IF EXISTS(SELECT * FROM sys.tables WHERE name = 'TBL_2PROC') DROP TABLE TBL_2PROC go CREATE TABLE TBL_2PROC( session_id INT, -- the session ID - it's expected each app transaction (or session) to have -- its own, exclusive, not shared, connection, even if pooled transaction_id BIGINT, -- to assure grouping -> (session_id, transaction_id) event VARCHAR(255), -- string stating an INSERT, UPDATE or DELETE tblName VARCHAR(255), -- name of the table affected keyId VARCHAR(255) -- value of the (single) id column, converted to varchar ) go
確保沒有棄用的觸發器存活
在創建實際觸發器之前,我們必須確保沒有留下舊觸發器:
-- Clean-up existing ones ------------------------------------------------------ -------------------------------------------------------------------------------- DECLARE cCursor CURSOR LOCAL FAST_FORWARD FOR SELECT name FROM sys.triggers WHERE name LIKE 'TR_2PROC%' -- DECLARE @trName VARCHAR(255) -- DECLARE @sql NVARCHAR(MAX) OPEN cCursor FETCH cCursor INTO @trName WHILE @@FETCH_STATUS = 0 BEGIN SET @sql = 'DROP TRIGGER [' + @trName + ']' EXECUTE sp_executesql @sql FETCH cCursor INTO @trName END go
參數化目標
在以程式方式創建所需的觸發器之前,我們必須聲明它們將是:
-- Parameterize the target tables and theirs key columns ----------------------- -------------------------------------------------------------------------------- CREATE TABLE #to_log(tblName VARCHAR(255), keyName VARCHAR(255)) go INSERT INTO #to_log VALUES('SomeTable', 'Id') INSERT INTO #to_log VALUES('SomeOtherTable', 'OtherId') go
以程式方式創建所需的觸發器
作為目標集,此腳本創建觸發器:
-- Setup triggers -------------------------------------------------------------- -------------------------------------------------------------------------------- DECLARE @tblName VARCHAR(255) DECLARE @keyName VARCHAR(255) -- DECLARE @sql NVARCHAR(MAX) -- DECLARE cCursor CURSOR LOCAL FAST_FORWARD FOR SELECT tblName, keyName FROM #to_log OPEN cCursor FETCH cCursor INTO @tblName, @keyName WHILE @@FETCH_STATUS = 0 BEGIN DECLARE @triggerName VARCHAR(255) SET @triggerName = '[TR_2PROC_' + @tblName + ']' -- Drop if exists ---------------------------------------------------------- SET @sql = N' IF EXISTS(SELECT * FROM sys.triggers WHERE name = ''' + @triggerName + ''') DROP TRIGGER ' + @triggerName EXECUTE sp_executesql @sql -- Create ------------------------------------------------------------------ SET @sql = N' CREATE TRIGGER ' + @triggerName + ' ON [' + @tblName + '] AFTER INSERT, UPDATE, DELETE AS BEGIN DECLARE @session_id INT DECLARE @transaction_id BIGINT SELECT @session_id = session_id, @transaction_id = transaction_id FROM sys.dm_tran_session_transactions WHERE session_id = @@spid INSERT INTO TBL_2PROC SELECT @session_id, @transaction_id, CASE WHEN del.[' + @keyName + '] IS NULL THEN ''INSERT'' ELSE ''UPDATE'' END, ''' + @tblName + ''', CONVERT(VARCHAR(255), ins.[' + @keyName + ']) FROM inserted ins LEFT OUTER JOIN deleted del ON del.[' + @keyName + '] = ins.[' + @keyName + '] INSERT INTO TBL_2PROC SELECT @session_id, @transaction_id, ''DELETE'', ''' + @tblName + ''', CONVERT(VARCHAR(255), del.[' + @keyName + ']) FROM deleted del WHERE del.[' + @keyName + '] NOT IN( SELECT ins.[' + @keyName + '] FROM inserted ins ) END' EXECUTE sp_executesql @sql FETCH cCursor INTO @tblName, @keyName END GO
一些清理
接下來是一些清理工作:
-- Clean-up -------------------------------------------------------------------- -------------------------------------------------------------------------------- DROP TABLE #to_log go
…並且,處理髮生在這裡!
現在,真正的交易。
最後但並非最不重要的一點是,您需要編寫處理程序。
即使可以
COMMIT
通過觸發器攔截 a,要處理的結果輸出也將與此非常相似。IF EXISTS(SELECT * FROM sys.procedures WHERE name = 'SP_2PROC') DROP PROCEDURE SP_2PROC GO CREATE PROCEDURE SP_2PROC AS BEGIN -- Important! -- -- Use WITH(NOLOCK) to avoid dead-locks -- -- Do NOT use SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED here, -- unless you know what you are doing -- (this isolation level is often safe in other contexts) -- You, now, shall build your process up from here, based on the -- query below SELECT * FROM TBL_2PROC WITH(NOLOCK) WHERE session_id = @@SPID END GO