有趣,但我只是碰巧在玩这个:
create trigger dbo.Things_Log on dbo.Things after Delete, Insert, Update as
declare @Now as DateTimeOffset = SysDateTimeOffset();
-- Determine the action that fired the trigger.
declare @Action VarChar(6) =
case
when exists ( select 42 from inserted ) and exists ( select 42 from deleted ) then 'update'
when exists ( select 42 from inserted ) then 'insert'
when exists ( select 42 from deleted ) then 'delete'
else NULL end;
if @Action is NULL
return;
-- Assign a unique value to group the log rows for this trigger firing.
declare @TriggerId as Int;
update TriggerIds
set @TriggerId = TriggerId += 1;
-- Log the data.
if @Action in ( 'delete', 'update' )
insert into ThingsLog
select @Action + '-deleted', @TriggerId, @Now, dbo.OriginalLoginName(), ThingId, ThingName
from deleted;
if @Action in ( 'insert', 'update' )
insert into ThingsLog
select @Action + '-inserted', @TriggerId, @Now, dbo.OriginalLoginName(), ThingId, ThingName
from inserted;
go
-- Logging triggers should always fire last.
execute sp_settriggerorder @triggername = 'dbo.Things_Log', @order = 'Last', @stmttype = 'DELETE';
execute sp_settriggerorder @triggername = 'dbo.Things_Log', @order = 'Last', @stmttype = 'INSERT';
execute sp_settriggerorder @triggername = 'dbo.Things_Log', @order = 'Last', @stmttype = 'UPDATE';
go
上下文:
create function [dbo].[OriginalLoginName]()
returns NVarChar(128)
as
begin
-- Returns the original login used to create the current session: Domain\username or sqlusername.
-- This function is not affected by impersonation.
-- Requires granting execute access to [public] and represents a diminutive security hole.
declare @Result as NVarChar(128);
select @Result = original_login_name
from sys.dm_exec_sessions
where session_id = @@SPID;
return @Result;
end;
go
CREATE TABLE [dbo].[Things](
[ThingId] [int] IDENTITY(1,1) NOT NULL,
[ThingName] [varchar](16) NOT NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[ThingsLog](
[ThingsLogId] [int] IDENTITY(1,1) NOT NULL,
[Action] [varchar](16) NOT NULL,
[TriggerId] [int] NOT NULL,
[TriggerTime] [datetimeoffset](7) NOT NULL,
[OriginalLoginName] [nvarchar](128) NOT NULL,
[ThingId] [int] NOT NULL,
[ThingName] [varchar](16) NOT NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[TriggerIds](
[TriggerId] [int] NULL
) ON [PRIMARY]
GO
insert into dbo.TriggerIds ( TriggerId ) values ( 0 );
日志记录触发器应配置为最后触发。这可以防止记录可能随后被其他触发器回滚的操作。对于奖励积分,可以报告未配置为最后触发的日志记录触发器的查询(假设您对触发器有一致的命名约定,例如 TableName_Log):
select PO.name as TableName, O.name as TriggerName, TE.type_desc,
case when O.name like PO.name + '_Log%' then 1 else 0 end as LoggingTrigger,
case when O.name like PO.name + '_Log%' and TE.is_last = 0 then 1 else 0 end as Misconfigured,
'' as [-], PO.type_desc as TableType, T.is_disabled, TE.is_first, TE.is_last, T.is_instead_of_trigger
from sys.objects as O inner join
sys.triggers as T on T.object_id = O.object_id inner join
sys.objects as PO on PO.object_id = T.parent_id inner join
sys.trigger_events as TE on TE.object_id = T.object_id
where
PO.type = 'U' and -- User table.
T.parent_class = 1 and -- Object or column trigger.
T.is_disabled = 0 and -- Is not disabled.
T.is_instead_of_trigger = 0 -- AFTER, not INSTEAD OF, trigger.
order by PO.name, O.name, TE.type_desc;
它可以合并到一个存储过程中,以纠正日志触发器的触发顺序。