我在表中有一个触发器,它保留所有更改(插入,更新,删除)。当我每次只插入一行时它工作正常。但是,当我尝试一次插入多行时,我收到此错误:
子查询返回的值超过1。这是不允许的 子查询跟随=,!=,<,< =,>,> =或当子查询用作 表达。
这是触发器的代码(我删除了一些不需要缩短代码的部分,如变量声明等。)
更新: 当#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([' +@Item +'] 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.[' +@Item +'] 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 :(得分: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([' +@Item +'] 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