不可行,因为DDL触发器本身是数据库对象,在目标库创建的触发器会在DROP/ALTER自身时立即失效,导致监控中断甚至报错;须用服务器级触发器并排除系统库,通过EVENTDATA()解析事件。

直接回答:用 DDL 触发器监控 DDL 触发器本身的变更(比如 DROP TRIGGER、ALTER TRIGGER)是可行的,但必须避开自身被删/改时的“自毁陷阱”——不能把触发器建在目标数据库里去监听自己,得用服务器级触发器 + 排除 tempdb 和系统库,且必须用 EVENTDATA() 解析事件类型。
为什么不能在目标数据库建触发器监听自身?
DDL 触发器本身是数据库对象。如果在 MyDB 中创建一个数据库级触发器来监听 ALTER_TRIGER 或 DROP_TRIGGER,当有人执行 DROP TRIGGER MyDB.dbo.trg_audit 时,这个触发器就立刻消失,后续事件再也不会被捕获。更糟的是,如果触发器里有写日志逻辑,而日志表又在同一个库中,删触发器前没关掉它,还可能报错中断操作。
- 服务器级触发器(
ON ALL SERVER)不受单个数据库状态影响,只要 SQL Server 实例活着就能响应 - 必须过滤掉
tempdb、master、msdb等系统库,否则会捕获大量无关事件(如 SSMS 自动生成的临时对象) -
EVENTDATA()返回 XML,需用.value()提取/EVENT_INSTANCE/EventType,不能靠OBJECT_NAME()反查——DDL 触发器不生成inserted/deleted表,也不支持OBJECT_ID()查自己
如何正确捕获 DROP TRIGGER 和 ALTER TRIGGER 事件?
SQL Server 把触发器变更归类为 CREATE_TRIGGER、ALTER_TRIGGER、DROP_TRIGGER 三类事件,但注意:CREATE_TRIGGER 不仅包括新建,也包括 CREATE OR ALTER TRIGGER ——后者本质是先删后建,会触发两次事件(DROP + CREATE),必须靠 PostTime 和 TSQLCommand 内容做合并判断。
- 建一张跨库日志表,推荐放在
master或独立审计库(如AuditDB),字段至少含:event_data xml、event_type nvarchar(100)、database_name nvarchar(128)、schema_name nvarchar(128)、object_name nvarchar(128)、post_time datetime、login_name nvarchar(128) - 服务器级触发器中必须加判断:
IF eventdata().value('(/EVENT_INSTANCE/DatabaseName)[1]', 'sysname') IN ('tempdb', 'master', 'model', 'msdb') RETURN - 提取关键字段示例:
eventdata().value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(100)')、eventdata().value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname')、eventdata().value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'nvarchar(max)')
容易踩的坑:EVENTDATA() 的生命周期和权限问题
EVENTDATA() 只在触发器执行期间有效,不能存到变量后再延迟处理;而且它返回的 XML 里不包含执行语句的完整上下文(比如跨库引用的 USE otherdb; DROP TRIGGER dbo.t,DatabaseName 字段仍显示当前 USE 的库,不是真正被删触发器所在的库)。
- 别在触发器里调用远程存储过程或链接服务器——网络延迟或超时会导致 DDL 语句整体失败(DDL 触发器默认是
AFTER,失败即回滚) - 写日志表的账号必须对目标表有
INSERT权限,且该权限不能依赖于被删的触发器所在数据库的角色(建议用sysadmin或显式授权给AuditDB) - 不要用
TRY...CATCH包裹整个日志插入逻辑——DDL 触发器内异常会直接让原 DDL 失败,应只对日志写入部分做轻量容错(如IF EXISTS(SELECT 1 FROM sys.databases WHERE name = 'AuditDB'))
最常被忽略的一点:ALTER TRIGGER 事件无法区分是修改逻辑还是改名(sp_rename 不触发它),而 DROP TRIGGER 也捕获不到通过 sp_dropextendedproperty 删除描述信息这类“软变更”。真要全覆盖,得配合 SQL Server Audit 或查询默认跟踪(fn_trace_gettable),但那是另一层开销了。

















