2013-08-20 94 views
1

我一直在爲這幾天摔跤。我有一個表格觸發器,爲每個插入,更新或刪除操作創建一個審計記錄,並將其放置在相鄰數據庫的相應審計表中。這個新的審計記錄包含源表主鍵值,操作類型,記錄的XML快照,當前日期和當前用戶。多行插入時SQL觸發器觸發不正確

上面列出的每條信息都是從INSERTED \ DELETED表中分配\計算出來的,並分配給參數值,然後傳遞給存儲過程,執行一段動態SQL以插入新記錄。對於這個問題的緣故,動態SQL是一個簡單的INSERT語句(如下圖所示):

 INSERT INTO [340bAudit].[aud].[TableName] 
      (
       RecordID 
       ,ActionType 
       ,xml_snapshot 
       ,ModifiedDate 
       ,ModifiedBy 
      ) 
      VALUES (@RecordPK 
       ,@action 
       ,@data 
       ,GETDATE() 
       ,SYSTEM_USER 
        ) 

這觸發出現罰款,只要工作,因爲只有被更新的記錄,但一旦發生情況多個記錄正在更新在一個單一的聲明,我得到這個:

子查詢返回超過1個值。當子查詢 跟隨=,!=,<,< =,>,> =或當子查詢用作 表達式時,這是不允許的。該語句已終止。

觸發看起來是這樣的:

ALTER TRIGGER [dbo].[RollOver_onUpdate] ON [dbo].[RollOver] 
AFTER INSERT, update, DELETE 
FOR EACH ROW 

AS 

BEGIN 

/******** Audit *******/ 

DECLARE @TableName varchar(50) 
     ,@RecordPK varchar(10) 
     ,@action char(1) 
     ,@data xml 

SET @TableName = 'RollOver' 
SET @RecordPK = (SELECT DISTINCT RollOverID FROM INSERTED) 

SET @action = 'I'; -- Set Action to Insert by default. 
IF EXISTS (SELECT * FROM DELETED) 
    BEGIN 
     SET @action = 
      CASE 
       WHEN EXISTS (SELECT * FROM INSERTED) 
        THEN 'U' -- Set Action to Updated. 
      ELSE 'D' -- Set Action to Deleted.  
      END 
    END 

SET @data = CASE WHEN @action <> 'D' THEN (SELECT'<rows>' + (SELECT * FROM INSERTED i FOR xml PATH) + '</rows>') 
       ELSE (SELECT'<rows>' + (SELECT * FROM DELETED d FOR xml PATH) + '</rows>') 
      END 

--Execute Audit Record Creation 
EXECUTE sp_CreateAuditRecord @Table = @TableName 
           ,@RecordID = @RecordPK 
           ,@ActionType = @action 
           ,@XML = @data 


END 

我可以改變此觸發由排在這種情況下執行?如果不是,我該從哪裏出發?

我非常想保留我與dyanamic SQL的靈活性,因爲我的數據庫中的每個表都使用相同的審計邏輯,並且這是第一個也是唯一一個看來有問題的表,到這個表的'使用'。

+1

看看最佳實踐:編碼SQL Server的多行操作觸發http://blogs.lessthandot.com/index.php/DataMgmt/DBProgramming/MSSQLServer/best-practice-coding-sql-服務器觸發器使其基於設置和擺脫proc電話 – SQLMenace

+0

謝謝,我會看看這個。 – drummer0512

+0

看起來這個鏈接不再有效。任何人都有類似資源的替代鏈接。 – drummer0512

回答

0

這就是我想出來的。這絕對不是最優雅的代碼,但它起作用並解決了我的問題。感謝您的回覆。我會投一個,但沒有人發佈一個答案讓我接受。

/******** Audit *******/ 

DECLARE @action char(1) 

SET @action = 'I'; -- Set Action to Insert by default. 

     IF @@ROWCOUNT = 0 
     RETURN 

     IF EXISTS (SELECT * FROM DELETED) 
      BEGIN 
       SET @action = CASE WHEN EXISTS (SELECT * FROM INSERTED) 
             THEN 'U' -- Set Action to Updated. 
            ELSE 'D' -- Set Action to Deleted.  
           END 

       INSERT INTO [340bAudit].[aud].[Orders] 
        (
         RecordID 
         ,ActionType 
         ,xml_snapshot 
         ,ModifiedDate 
         ,ModifiedBy 
        ) 
       SELECT d.OrderID, 
         CASE WHEN EXISTS (SELECT * FROM INSERTED) 
             THEN 'U' -- Set Action to Updated. 
            ELSE 'D' -- Set Action to Deleted.  
           END , 
         CASE WHEN @action <> 'D' THEN (SELECT'<rows>' + (SELECT * FROM INSERTED i FOR xml PATH) + '</rows>') 
          ELSE (SELECT'<rows>' + (SELECT * FROM DELETED d FOR xml PATH) + '</rows>') END, 
         GETDATE(), 
         SYSTEM_USER 
       FROM DELETED d 
      END 

     -- INSERTED Order Records 
     INSERT INTO [340bAudit].[aud].[Orders] 
      (
       RecordID 
       ,ActionType 
       ,xml_snapshot 
       ,ModifiedDate 
       ,ModifiedBy 
      ) 
     SELECT i.OrderID, 
       @action, 
       (SELECT'<rows>' + (SELECT * FROM INSERTED i FOR xml PATH) + '</rows>'), 
       GETDATE(), 
       SYSTEM_USER 
     FROM INSERTED i