2017-10-06 157 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