SQL Server 中唯一能捕获 CREATE TABLE、ALTER TABLE、DROP TABLE 的机制是数据库级 DDL 触发器,语法为 CREATE TRIGGER xxx ON DATABASE FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE,必须用 EVENTDATA() 解析事件并过滤 tempdb,禁止阻塞操作,告警仅能记录日志由外部程序处理。

SQL Server 中唯一能捕获 CREATE TABLE、ALTER TABLE、DROP TABLE 这类操作的机制,是数据库级 DDL 触发器——不是表上的 AFTER UPDATE,也不是存储过程或轮询脚本。
必须用 ON DATABASE FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE
DDL 触发器只能建在数据库作用域,语法形如 CREATE TRIGGER xxx ON DATABASE FOR ALTER_TABLE。写成 ON users 或 ON SCHEMA::dbo 都无效;用 AFTER 或 INSTEAD OF 也会报错,因为 DDL 触发器只认 FOR 关键字。
- 触发器内必须调用
EVENTDATA()提取 XML,再用.value()解析关键字段,比如:EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]', 'sysname') - 务必过滤
tempdb:开头加IF EVENTDATA().value('(/EVENT_INSTANCE/DatabaseName)[1]', 'sysname') = 'tempdb' RETURN,否则 SSMS 临时对象会刷爆日志 - 不要在触发器里调用
sp_send_dbmail或EXEC xp_cmdshell:它们会阻塞 DDL 事务,导致ALTER TABLE卡死甚至超时回滚
告警动作只能写入本地日志表,不能直接发通知
触发器运行在事务上下文中,禁止任何阻塞型 I/O。所谓“自动告警”,实际只能做到“自动记录”,后续由外部程序(如 PowerShell 脚本、SQL Agent Job 或 .NET 服务)轮询日志表并真正发邮件/钉钉/Webhook。
- 推荐建一张轻量表,例如:
ddl_change_log,至少含字段:event_time DATETIME2、event_type NVARCHAR(50)、object_name NVARCHAR(128)、tsql_command NVARCHAR(MAX)、login_name NVARCHAR(128) - 插入语句要包裹在
TRY...CATCH中:XML 解析可能失败(比如 TSQLCommand 被截断),不捕获会导致整个 DDL 失败 - 别存完整 XML:用
CONVERT(NVARCHAR(MAX), EVENTDATA())可能超长,建议只提取必要字段并拼接字符串
权限和部署容易被忽略的三个点
DDL 触发器不是建完就生效,权限、作用域和启用状态缺一不可。
- 创建者必须有
CREATE DATABASE DDL TRIGGER权限;若日志表跨库,还需ALTER ANY DATABASE - 触发器默认启用,但可通过
DISABLE TRIGGER ... ON DATABASE关闭——上线后没人检查这个状态,出问题时第一反应常是“触发器没起作用”,其实是被手动禁用了 - SSMS 图形界面改列名、索引、约束等操作,多数走系统存储过程路径(如
sp_rename),不触发ALTER_TABLE事件,需额外监听RENAME类事件或靠权限控制兜底
真正难的不是写触发器,而是让告警不误报、不漏报、不拖慢业务。比如一个 ALTER TABLE ADD COLUMN 语句,在高并发场景下可能被多个会话同时执行,而触发器没有天然去重机制;又比如 EVENTDATA() 的 TSQLCommand 字段默认只保留前 4000 字符,复杂 ALTER 会被截断——这些细节不处理,告警日志就只是看起来热闹而已。










