使用inserted和deleted表的一个警告是它们都可以为空。还有其他任何我应该注意的catchya吗?例如,插入的表是否可以包含新记录以及更新的记录?
我依靠这个逻辑来检测触发器中的动作:
IF EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted) SET @operation = 'U'
IF EXISTS(SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted) SET @operation = 'I'
IF NOT EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted) SET @operation = 'D'
IF NOT EXISTS(SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted) SET @operation = 'X'
编辑:
这是我对审计跟踪问题的解决方案。它已在一个MERGE语句中进行了测试,该语句插入,伪造更新,实际更新和删除。
ALTER TRIGGER [dbo].[LogInsertEditDelete]
ON [dbo].[<TableToAudit>]
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--You will need to change @table to match the table to be audited
DECLARE @table VARCHAR(50)
SELECT @table = '<TableToAudit>'
-- date and user
DECLARE @updatedBy VARCHAR(50),
@timestamp DateTime
SELECT @updatedBy = SYSTEM_USER,
@timestamp = GETDATE()
-- Action, U = update, I = insert, D = delete
DECLARE @insertedCount int,
@deletedCount int
SET @insertedCount = (SELECT COUNT(*) FROM inserted)
SET @deletedCount = (SELECT COUNT(*) FROM deleted)
-- handle no action
IF @insertedCount = 0 AND @deletedCount = 0 RETURN
-- handle update
IF @insertedCount <> 0 AND @deletedCount <> 0
BEGIN
INSERT Audit (Type, TableName, UpdateDate, UpdatedBy, PK1)
SELECT
'U',
@table,
@timestamp,
@updatedBy,
CONVERT(VARCHAR(255), i.Id)
FROM
(SELECT Id, BINARY_CHECKSUM(*) Version FROM inserted) i
INNER JOIN
(SELECT Id, BINARY_CHECKSUM(*) Version FROM deleted) d
ON i.Id = d.Id
WHERE
i.Version <> d.Version
RETURN
END
-- handle deletes and inserts
INSERT Audit (Type, TableName, UpdateDate, UpdatedBy, PK1)
SELECT
CASE
WHEN i.Id IS NOT NULL AND d.Id IS NULL THEN 'I'
WHEN i.Id IS NULL AND d.Id IS NOT NULL THEN 'D'
END,
@table,
@timestamp,
@updatedBy,
CONVERT(VARCHAR(255), COALESCE(i.Id, d.Id))
FROM inserted i
FULL OUTER JOIN
deleted d
ON i.Id = d.Id
WHERE i.Id IS NULL OR
d.Id IS NULL
END
此解决方案不是通用的,因为需要为每个表拼写主键。
答案 0 :(得分:1)
所有4个案例都是可能的。值得注意的是&#34;两者都是&#34;在更新和&#34;无&#34;如果DML语句没有修改任何行。
使用EXISTS
查询可以检测到最后一个条件更便宜:
if @@ROWCOUNT = 0
return
您可以重新构建EXIST
- 稍微检查一下,以便每个表只查询一次,以提高效率。
答案 1 :(得分:1)
在发表评论之后,我对MERGE中插入和删除表中的内容感到好奇。这是一个例子:
create table #test (test1 int identity not null, test2 varchar(34) null)
Insert into #test (test2)
values('test'), ('test2')
select * from #test
declare @output table(test1I int, test1D int, test2I varchar (34), test2D varchar (34))
MERGE #test AS target
USING (SELECT test1, test2 from #test ) AS source (test1, test2)
ON (target.test1 = source.test1)
WHEN MATCHED AND target.test1 = 1
THEN UPDATE SET target.test2 = 'test3'
WHEN MATCHED
THEN DELETE
OUTPUT inserted.test1, deleted.test1, inserted.test2, deleted.test2 into @output ;
select 'Inserted/deleted contents', * from @output
select * from #test
Drop table #test
如果您正在进行插入或更新,会发生类似情况。
如果您需要知道每条记录,您可能希望使用连接和案例陈述来计算每条记录的状态,而不是使用标量变量。记住触发器对插入/更新/删除的整组记录进行操作,而不是一次只记录一条记录。因此,标量变量的使用通常是您没有正确处理事情的线索。如果你给我们更多关于你的触发器将要做什么的例子,那么帮助你解决问题会更容易,因为当前的方法似乎不会覆盖所有记录。
根据您上面的评论,或许这会为您提供有关触发器内尝试内容的一些想法:
Insert dbo.Audittable (Id, NewField1Value, OldField1Value, ActionTaken, ActionDate, ActionUser)
select coalesce(inserted.id, deleted.id) as Id, inserted.field1 as newField1value, deleted.field1 as oldField1value ,
case when inserted.id is not null and deleted.id is not null then 'Update'
when inserted.id is not null and deleted.id is null then 'Insert'
when inserted.id is null and deleted.id is not null then 'Delete'
End as ActionTaken
, getdate() as ActionDate
, system_user as ActionUser
from inserted
full outer join deleted on inserted.id = deleted.id
测试触发器时,至少需要以下测试用例:
根据触发器的所有内容,您可能需要更多。