2017-10-06 145 views
0

我在表中保留了所有更改(插入,更新,刪除)的觸發器。當我每次只插入一行時,它工作正常。但是當我試圖一次插入多行時,我收到此錯誤:SQL Server 2012觸發器:爲每一行執行動態sql

子查詢返回多個值。當 子查詢遵循=,!=,<,< =,>,> =或當子查詢用作 表達式時,這是不允許的。

這裏是觸發器的代碼(I除去不需要的縮短等變量聲明等代碼中的一些部件)

UPDATE:實際的錯誤是在這些線時#tempTrigT包含多行:

Select * into #tempTrigT from (select * from deleted where @Action in ('U','D')) A UNION (select * from inserted where @Action ='I') 

    set @sql = 'set @audit_oldvalue=(select cast([' [email protected] +'] as NVARCHAR(4000)) from #tempTrigT)'; 
    EXEC SP_EXECUTESQL @sql,N'@audit_oldvalue sql_variant OUTPUT',@audit_oldvalue OUTPUT -- If inserted @audit_oldvalue gets the new value 

    set @sql = 'set @audit_value=(select cast(i.[' [email protected] +'] as NVARCHAR(4000)) from dbo.TForms i inner join #tempTrigT d on i.id = d.id)'; 
    EXEC SP_EXECUTESQL @sql,N'@audit_value sql_variant OUTPUT',@audit_value OUTPUT 

我該如何改變它以適用於多行?

+0

所以..任何時候你得到這個錯誤,只要使用'='查找子查詢。你有兩個:'set @ audit_oldvalue =(從#tempTrigT)'和下面的'set'操作選擇cast(['+ @ Item +']作爲NVARCHAR(4000))。這兩個看起來像他們會拉我多重價值。 –

+0

@AaronDietz,但我不應該收到這個錯誤...對不對? – aggicd

+0

如果您使用'=(subquery)'並且該子查詢返回多個值,您將收到錯誤 –

回答

2

您缺少一個行標識符,以便每個循環只處理一行。喜歡的東西:

  • DECLARE @ID int = (SELECT MIN(id) FROM #tempTrigT)在你的循環的開始定義一個排
  • WHERE id = @ID過濾到該行整個環
  • DELETE FROM #tempTrigT WHERE id = @ID在你的循環結束,當id做處理

然後,如果id可以在#tempTrigT中重複,那麼這可能甚至不起作用。

並與所有的說...

我肯定會考慮這個分成多個觸發器,並保存自己的,你所面臨的複雜性通過刪除或插入的記錄試圖環和相應的處理它們。我也會考慮簡化審計過程。最終的目標是能夠回頭看看哪些記錄曾經是,你可以真正做到簡單:

INSERT INTO [dbo].[AuditTrailTForms] (TForms_Cols, ChangeDate, Change_User, Change_Type) 
    SELECT T.*, GETDATE(), COALESCE(ModifiedBy,suser_name()), 'Inserted' 
    FROM inserted i 
    JOIN TForms T on i.id = T.id 

然後你可以不用擔心使它更容易查看其列值當您查詢在後來改這些表:

SELECT * 
FROM (SELECT *, GETDATE(), 'Current', 'Current' 
     FROM TForms 
     WHERE ID = @AuditID 
     UNION ALL 
     SELECT * 
     FROM AuditTrailTForms 
     WHERE ID = @AuditID 
     --AND Change_Type = 
     --AND Change_User = 
    ) T 
ORDER BY ChangeDate DESC 

編輯:使用標識列

您可以使用標識列定義一排像這樣每次循環:

DECLARE @TotalRows int = (SELECT MAX(identityColumn) FROM #tempTrigT 
DECLARE @RowID int = 1 
WHILE @RowID <= @TotalRows 
    BEGIN 
     --Do stuff 
     --For Example 
      SET @sql = 'set @audit_oldvalue=(SELECT cast([' [email protected] +'] as NVARCHAR(4000)) 
              FROM #tempTrigT 
              WHERE T.IdentityColumn = @RowID)'; 
      EXEC SP_EXECUTESQL @sql,N'@audit_oldvalue sql_variant OUTPUT',@audit_oldvalue OUTPUT 

     --then increment to the next row when you're done 
     SET @RowID = @RowID + 1 
    END 
+0

關於您的帖子的第一部分。如果我在#tempTrigT表中插入標識列,它會起作用嗎? – aggicd

+0

@aggicd是的,任何限制每個循環到一個單行將會工作 –

+0

是否有可能提供一個基於此解決方案?添加標識列? – aggicd